Jump to content

[1.3.2][SOLVED] Problem with saving/loading block's data in NBT


bormoshka

Recommended Posts

Hello again. I have a problem with saving value of TileEntity. I have items and blocks with metadata, so I can't storage angle there. When I place block - it's OK. But, when i reenter - all blocks rotated at 0 angle. Maybe I missed something.

Before:

 

before.png

 

After:

 

after.png

 

BlockFlag.java:

public class BlockFlag extends Block
{
private byte flagType;
private Class flagEntityClass;
private byte angle;
public BlockFlag(int i, Class class1)
    {
            super(i, Material.cloth);
            flagEntityClass = class1;
            setHardness(1.5F);
            setResistance(1.5F);
            setStepSound(Block.soundWoodFootstep);
            setBlockName("Minecraft Standart");
            this.setTextureFile(getTextureFile());
            blockIndexInTexture = 5;
            this.setRequiresSelfNotify();
            this.isBlockContainer = true;
    }
    public void setValue(Item anItemDrop, byte aFlagType)
    {
    	flagType = aFlagType;
    	//flagMetadata = aFlagMetadata;
    }
    public TileEntity getBlockEntity()
    {
            try
            {
                    return (TileEntity)flagEntityClass.newInstance();
            }
            catch (Exception exception)
            {
                    throw new RuntimeException(exception);
            }
    }
    public TileEntity createTileEntity(World world, int metadata)
    {
    	TileEntityFlag myTile = new TileEntityFlag();
    	myTile.setValues(flagType);
    	myTile.onInventoryChanged();
    	return myTile;
    }   
    public TileEntity createNewTileEntity(World var1)
    {
    	TileEntityFlag tileEntityFlag = new TileEntityFlag();
    	return tileEntityFlag;
    }
    public AxisAlignedBB getCollisionBoundingBoxFromPool(World par1World, int par2, int par3, int par4)
    {
// NOT IMPORTANT
    }
    private boolean canPlaceFlagOn(World par1World, int par2, int par3, int par4)
    {
// NOT IMPORTANT
    }
    protected int damageDropped(int par1)
    {
        return par1;
    }
    public boolean canPlaceBlockAt(World par1World, int par2, int par3, int par4)
    {
        return canPlaceFlagOn(par1World, par2, par3 - 1, par4);
    }
    private boolean dropFlagIfCantStay(World par1World, int par2, int par3, int par4)
    {
// NOT IMPORTANT
    }
    public void onNeighborBlockChange(World par1World, int par2, int par3, int par4, int par5)
    {
// NOT IMPORTANT
    }
    public int idDropped(int i, Random random, int j)
    {
            return UltimateExtender.itemFlag.shiftedIndex;
    }
    @SideOnly(Side.CLIENT)

    /**
     * returns a list of blocks with the same ID, but different meta (eg: wood returns 4 blocks)
     */
    public void getSubBlocks(int par1, CreativeTabs par2CreativeTabs, List par3List)
    {
// NOT IMPORTANT
    }
    public int quantityDropped(Random random)
    {
            return 1;
    }
    public int getRenderType()
    {
            return -1;
    }
    @Override
    public String getTextureFile()
    {
        return Params.TEXTURE_PATH_BLOCKS;
    }
    public boolean isOpaqueCube()
    {
            return false;
    }
    public boolean renderAsNormalBlock()
    {
            return false;
    }
    public void onBlockAdded(World par1World, int par2, int par3, int par4)
    {
        super.onBlockAdded(par1World, par2, par3, par4);
        TileEntityFlag myTile = (TileEntityFlag)this.createTileEntity(par1World, par1World.getBlockMetadata(par2, par3, par4));
        par1World.setBlockTileEntity(par2, par3, par4, myTile);        
    }
    public void onBlockPlacedBy(World par1World, int par2, int par3, int par4, EntityLiving par5EntityLiving)
    {
    	TileEntityFlag myTile = (TileEntityFlag)par1World.getBlockTileEntity(par2, par3, par4);
    	this.angle = (byte) (MathHelper.floor_double((double)(par5EntityLiving.rotationYaw + 180.0F) * 16.0F / 360.0F) & 15);
    	myTile.angle = this.angle;
    	myTile.onInventoryChanged();
    }
    public void breakBlock(World par1World, int par2, int par3, int par4, int par5, int par6)
    {
        super.breakBlock(par1World, par2, par3, par4, par5, par6);
        par1World.removeBlockTileEntity(par2, par3, par4);
    }
    
}

ItemFlag.java

public class ItemFlag extends Item
{
private int blockID;
public static String[] flagNames = new String[]{"flag1","flag2","flag3","flag4","flag5","flag6","flag7","flag8",
"flag9","flag10","flag11","flag12","flag13","flag14","flag15","flag16"};

    public ItemFlag(int i, Block block)
    {
        super(i);
        blockID = block.blockID;
        this.setHasSubtypes(true);
        this.setMaxDamage(0);
    }
    public int getIconFromDamage(int par1)
    {
    	int var2 = MathHelper.clamp_int(par1, 0, 15);
        return this.iconIndex + var2;
    }
    public String getItemNameIS(ItemStack par1ItemStack)
    {
        int var2 = MathHelper.clamp_int(par1ItemStack.getItemDamage(), 0, 15);
        return super.getItemName() + "." + flagNames[var2];
    }
    @SideOnly(Side.CLIENT)

    /**
     * returns a list of items with the same ID, but different meta (eg: dye returns 16 items)
     */
    public void getSubItems(int par1, CreativeTabs par2CreativeTabs, List par3List)
    {
        for (int var4 = 0; var4 < 16; ++var4)
        {
            par3List.add(new ItemStack(par1, 1, var4));
        }
    }
    public int getMetadata(int par1)
    {
        return par1;
    }
    public int getBlockID()
    {
        return this.blockID;
    }   
    public boolean tryPlaceIntoWorld(ItemStack par1ItemStack, EntityPlayer par2EntityPlayer, 
World par3World, int par4, int par5, int par6, int par7, float par8, float par9, float par10)
    {
        int var11 = par3World.getBlockId(par4, par5, par6);

        if (var11 == Block.snow.blockID)
        {
            par7 = 1;
        }
        else if (var11 != Block.vine.blockID && var11 != Block.tallGrass.blockID && var11 != Block.deadBush.blockID
                && (Block.blocksList[var11] == null || !Block.blocksList[var11].isBlockReplaceable(par3World, par4, par5, par6)))
        {
            if (par7 == 0)
            {
                --par5;
            }

            if (par7 == 1)
            {
                ++par5;
            }

            if (par7 == 2)
            {
                --par6;
            }

            if (par7 == 3)
            {
                ++par6;
            }

            if (par7 == 4)
            {
                --par4;
            }

            if (par7 == 5)
            {
                ++par4;
            }
        }

        if (par1ItemStack.stackSize == 0)
        {
            return false;
        }
        else if (!par2EntityPlayer.canPlayerEdit(par4, par5, par6))
        {
            return false;
        }
        else if (par5 == 255 && Block.blocksList[this.blockID].blockMaterial.isSolid())
        {
            return false;
        }
        else if (par3World.canPlaceEntityOnSide(this.blockID, par4, par5, par6, false, par7, par2EntityPlayer))
        {
            Block var12 = Block.blocksList[this.blockID];

            if (placeBlockAt(par1ItemStack, par2EntityPlayer, par3World, par4, par5, par6, par7, par8, par9, par10))
            {
                par3World.playSoundEffect((double)((float)par4 + 0.5F), 
(double)((float)par5 + 0.5F), (double)((float)par6 + 0.5F), var12.stepSound.getStepSound(), 
(var12.stepSound.getVolume() + 1.0F) / 2.0F, var12.stepSound.getPitch() * 0.8F);
                --par1ItemStack.stackSize;
            }

            return true;
        }
        else
        {
            return false;
        }
    }
    public boolean placeBlockAt(ItemStack stack, EntityPlayer player, World world, int x, int y, int z, int side, float hitX, float hitY, float hitZ)
    {
       if (!world.setBlockAndMetadataWithNotify(x, y, z, this.blockID, this.getMetadata(stack.getItemDamage())))
       {
               return false;
       }

       if (world.getBlockId(x, y, z) == this.blockID)
       {
           Block.blocksList[this.blockID].updateBlockMetadata(world, x, y, z, side, hitX, hitY, hitZ);
           Block.blocksList[this.blockID].onBlockPlacedBy(world, x, y, z, player);
       }

       return true;
    }
}

TileEntityFlag.java:

public class TileEntityFlag extends TileEntity
{
public byte angle;
private byte type;
    public void writeToNBT(NBTTagCompound par1NBTTagCompound)
    {
        super.writeToNBT(par1NBTTagCompound);
        par1NBTTagCompound.setByte("ang", this.angle);
        par1NBTTagCompound.setByte("type", this.type);
    }
    public void readFromNBT(NBTTagCompound par1NBTTagCompound)
    {
        super.readFromNBT(par1NBTTagCompound);
        this.type = par1NBTTagCompound.getByte("type");
        this.angle = par1NBTTagCompound.getByte("ang");
    }
    public void setAngle(byte i)
    {
        this.angle = i;
    }
    public int getAngle()
    {
        return this.angle;
    }
    public void setValues(byte i)
    {
        this.type = i;
    }
    public int getType()
    {
        return this.type;
    }
}

Link to comment
Share on other sites

I don't know why, but its not.

In Render i'm trying to get angle from TileEntity is it right?

public void renderFlag(TileEntity tileEntity, double d, double d1, double d2, float f)
    {
    	
    TileEntityFlag myTile = (TileEntityFlag)tileEntity;
    //System.out.println(myTile.getSkin());
    float deg = (float)(myTile.getAngle() * 360) / 16.0F;
    int i = myTile.getType();
    int j = myTile.getBlockMetadata();
    GL11.glPushMatrix();
    GL11.glTranslatef((float)d + 0.5F, (float)d1 + 1.5F, (float)d2 + 0.5F);
            GL11.glScalef(1.0F, 1.0F, 1.0F);
            GL11.glRotatef(180F, 0.0F, 0.0F, 1.0F);
            GL11.glRotatef(deg, 0.0F, 1.0F, 0.0F);
            bindTextureByName(getPath(i,j));
            modelFlag.render(0.0625F);
            GL11.glPopMatrix();
    }

 

 

Link to comment
Share on other sites

Might want to send a packet to the client with the angle in it so the render can get the updated angle. Also might want to send what ever other info the client needs for the render as well.

 

 

If you need help making packet stuff check out my gitup https://github.com/DarkGuardsman

Link to comment
Share on other sites

Thanks, I'll try. But I'm testing it in singleplayer and this value is saves and loads(after exit) propertly:

public TileEntity createTileEntity(World world, int metadata)

    {

    TileEntityFlag myTile = new TileEntityFlag();

    myTile.setValues(flagType);

    myTile.onInventoryChanged();

    return myTile;

    }

and this is not:

public void onBlockPlacedBy(World par1World, int par2, int par3, int par4, EntityLiving par5EntityLiving)

    {

    TileEntityFlag myTile = (TileEntityFlag)par1World.getBlockTileEntity(par2, par3, par4);

    this.angle = (byte) (MathHelper.floor_double((double)(par5EntityLiving.rotationYaw + 180.0F) * 16.0F / 360.0F) & 15);

    myTile.angle = this.angle;

    myTile.onInventoryChanged();

    }

Link to comment
Share on other sites

Thanks, I'll try. But I'm testing it in singleplayer and this value is saves and loads(after exit) propertly:

public TileEntity createTileEntity(World world, int metadata)

    {

    TileEntityFlag myTile = new TileEntityFlag();

    myTile.setValues(flagType);

    myTile.onInventoryChanged();

    return myTile;

    }

and this is not:

public void onBlockPlacedBy(World par1World, int par2, int par3, int par4, EntityLiving par5EntityLiving)

    {

    TileEntityFlag myTile = (TileEntityFlag)par1World.getBlockTileEntity(par2, par3, par4);

    this.angle = (byte) (MathHelper.floor_double((double)(par5EntityLiving.rotationYaw + 180.0F) * 16.0F / 360.0F) & 15);

    myTile.angle = this.angle;

    myTile.onInventoryChanged();

    }

You sure in 1.3.2 Minecraft single player is a lan server. I use to think it worked fine til a lot of my stuff stopped updating correctly. Namely the rotation of my Machines for several of my mods.

Link to comment
Share on other sites

You sure in 1.3.2 Minecraft single player is a lan server. I use to think it worked fine til a lot of my stuff stopped updating correctly. Namely the rotation of my Machines for several of my mods.

Ok, maybe when i create TileEntity and change some values in createTileEntity(World world, int metadata) it works, but if I trying to change it from client side - it fails. That's why I must create proper packet exchange. So I looked in your code, made something that look pretty the same... but it fails again. So can you or someone that reads this help with this? Maybe manuals or something?

 

Link to comment
Share on other sites

You sure in 1.3.2 Minecraft single player is a lan server. I use to think it worked fine til a lot of my stuff stopped updating correctly. Namely the rotation of my Machines for several of my mods.

Ok, maybe when i create TileEntity and change some values in createTileEntity(World world, int metadata) it works, but if I trying to change it from client side - it fails. That's why I must create proper packet exchange. So I looked in your code, made something that look pretty the same... but it fails again. So can you or someone that reads this help with this? Maybe manuals or something?

next chance i get i'll create a short tut on the subject, or at least raw code that can be copied.

Link to comment
Share on other sites

Ok, this if funny, I just went through this EXACT same problem, and for me the key lied in... signs! :-P

 

If you look at how the signs metadata works, that's EXACTLY what you want... so, here's some of the legwork done for ya':

 

Just look at

ItemSign.java for how your ItemFlag.java should be

BlockSign.java for how your BlockFlag.java should be

TileEntitySign.java for how your TileEntityFlag.java should be

 

Also, do NOT forget to do the following when your mod loads....

Make SURE you register your tileentity like this....

 

GameRegistry.registerTileEntity(TileEntityFlag.class, "ang");

 

Also, in your ItemFlag, you should probably pass the angle to it then using something like:

int myAng = MathHelper.floor_double((double)((par2EntityPlayer.rotationYaw + 180.0F) * 16.0F / 360.0F) + 0.5D) & 15;

(it just gets the angle from the player's angle)

 

Now let me know if you still can't sort it out, but I believe most of what you're doing should be covered by the sign example.

Also, you should really put the following line of code in your stuff so you can track what angle it's storing, and getting....

Minecraft mc = Minecraft.getMinecraft();
mc.thePlayer.sendChatMessage(""+ang);

 

Now lastly, here's a brief overview of how your metadata should be working:

When you place the flag ItemFlag gets the angle from the player, then it passes the data to the block's metadata

The TileEntityFlag just handles the reading and writing of the metadata (which seems kinda repetitive and unnecessary to me, but that's how it is! :-P)

The BlockFlag should then reads that metadata

 

Now.... with all that said... are you sure you can't just extend the "Sign" class, override the render type and a few things, and call it good? :-P LOL

 

Good luck to ya', let me know if you have any questions about my irrational ramblings! :-P

 

I struggled with this same exact problem for a week though, and it took me that long, to reach this level of insanity (and yes, I finally got it working last night, due to registering my tileentity class) :-)

 

 

 

Link to comment
Share on other sites

speaking of sign class there is a method in there to send a packet to the client with the sign text. You can change it up a bit to fit your needs.

 /**
     * signs and mobSpawners use this to send text and meta-data
     */
    public Packet getAuxillaryInfoPacket()
    {
        String[] var1 = new String[4];
        System.arraycopy(this.signText, 0, var1, 0, 4);
        return new Packet130UpdateSign(this.xCoord, this.yCoord, this.zCoord, var1);
    }

change packet to Packet250 or what ever the custom packet is. I  used this for a while till i need to update the server from the client. However if you want to do it the more reusable way, in which you can set it up to send any info to and from the server.

 

 

Your packet Handler class

public class PacketManager implements IPacketHandler
{
@Override
public void onPacketData(NetworkManager network, Packet250CustomPayload packet, Player player)
{
	try
        {
		ByteArrayDataInput data = ByteStreams.newDataInput(packet.data);
		int x = data.readInt();
		int y = data.readInt();
		int z = data.readInt();
                        //the following two are not used but can be for sorting packets by ID and restricting packet reading by lengths
		int id = data.readInt();//packet ID your welcome to set to zero 
		int l = data.readInt(); //packet length a safety var to make sure only data is read.
		EntityPlayer ePlayer = (EntityPlayer) player;
		if(ePlayer != null)
		{
			TileEntity tileEntity = ePlayer.worldObj.getBlockTileEntity(x, y, z);

			if(tileEntity != null)
			{
				if(tileEntity instanceof IPacketReceiver)
				{
					((IPacketReceiver) tileEntity). handlePacketData(network,packet.channel,
							data);

				}
			}
		}
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
}
/**
 * 
 * @param sender - TileEntity sending this packet
 * @param channelName - channel name "channelName"
 * @param id - packet id 
 * @param sendData - list of Integers to be sent is read after main data
 * @param string - list of strings to be sent is read last
 * @return a constructed packet ready to be sent
 */
     //If your looking for a better version check out Calclavia's PacketManager his is very well developed
public static Packet TECommonPacket(TileEntity sender, String channelName,int id, int[] sendData, String[] string)
    {
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        DataOutputStream data = new DataOutputStream(bytes);

        try
        {
            data.writeInt(sender.xCoord);
            data.writeInt(sender.yCoord);
            data.writeInt(sender.zCoord);
            data.writeInt(id);
            data.writeInt(3+sendData.length);
           //only writes ints and string you can change this to write anything
            for(int i =0; i < sendData.length; i++)
            {
                data.writeInt(sendData[i]);
            }
            for(int i =0; i < string.length; i++)
            {
                data.writeUTF(string[i]);
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }

        Packet250CustomPayload packet = new Packet250CustomPayload();
        packet.channel = "ChannelName";//TODO change to registered channel for you mod
        packet.data = bytes.toByteArray();
        packet.length = packet.data.length;
        packet.isChunkDataPacket = true;
        return packet;
    }

}

Here is the IPacketHandler interface. Implement this in every tileEntity you need to send packets with.

 

package npc.Network;
import net.minecraft.src.NetworkManager;

import com.google.common.io.ByteArrayDataInput;

public interface IPacketReceiver
{
    /**
     * Sends the tileEntity the rest of the data
     */
    public void handlePacketData(NetworkManager network, String channel, ByteArrayDataInput data);
}

Your tileEntity

public class TileEntity extends TileEntity{
   
    @Override
    public void updateEntity()
    {
//highly suggest setting a timer function to only send a packet every so often, also you can change send packetToAll to sendToAll around to reduce lag furthur
    	if(!worldObj.isRemote)
    	{
                      FMLCommonHandler.instance().getMinecraftServerInstance().getConfigurationManager().sendPacketToAllPlayers(getPacket());
		    	
    	}
    	
    	super.updateEntity();
    }
  
  public Packet getPacket()
{
	int[] data = this.getSendLoadInt(); ;
	if(data == null)
	{
		data = new int[]{};
	}
	String[] string = this.getSendLoadStr(); ;
	if(string == null)
	{
		string = new String[]{};
	}
	return PacketManager.TECommonPacket(this, "ChannelName",0, data,string );//TODO change channel name
}
   
 @Override
  public int[] getSendLoadInt()
	{
		return new int[] {angle,type,etc};	//TODO	//array that stores all int that need to be sent by packet make sure what is in here ir read in handlePacketData	
	}
 @Override
	public void handlePacketData(NetworkManager network, String channel, ByteArrayDataInput data) {

		if(worldObj.isRemote)
		 {
                              //this.angle = (int)data.readInt();
                              //this.type = (int)data.readInt();

		 }



	}
public void updateInfo(int[] sRange) {
	if(sRange.length == 5)
	{
	this.minX = sRange[1];
	this.minZ = sRange[2];
	this.maxX = sRange[3];
	this.maxZ = sRange[4];
	}

}


}
}

in you main mod class make sure you have this

@NetworkMod(channels = { "ChannelName" }, clientSideRequired = true, serverSideRequired = false, packetHandler = PacketManager.class)

 

 

Link to comment
Share on other sites

Bandayd, thanks for inchat debug. I can't use metadata - its used for skin. I've used metadata for storage rotation value, but here I cant =C

 

DarkGuardsman, Thanks! It works!

For history:

TileEntity

public class TileEntityFlag extends TileEntity implements IPacketReceiver
{
public int angle;
public int type;
    public void writeToNBT(NBTTagCompound par1NBTTagCompound)
    {
        super.writeToNBT(par1NBTTagCompound);
        par1NBTTagCompound.setInteger("type", this.type);
        par1NBTTagCompound.setInteger("ang", this.angle);
    }
    public void readFromNBT(NBTTagCompound par1NBTTagCompound)
    {
        super.readFromNBT(par1NBTTagCompound);
        this.type = par1NBTTagCompound.getInteger("type");
        this.angle = par1NBTTagCompound.getInteger("ang");
    }
    public int getAngle()
    {
        return this.angle;
    }
    public void setValues(byte i, byte j)
    {
        this.type = i;
        this.angle = j;
    }
    public int getType()
    {
        return this.type;
    }
    //method used for getting packet from PacketManager
public Packet getPacket()
{
	int[] data = this.getSendLoadInt(); ;
	if(data == null)
	{
		data = new int[]{};
	}
	return PacketManager.TECommonPacket(this, "UltimateExtender", 0, data);
}
public int[] getSendLoadInt()
	{
		return new int[] {angle,type};	//array that stores all int that need to be sent by packet make sure what is in here ir read in handlePacketData	
	}
//is it a standard method?
public Packet getAuxillaryInfoPacket()
    {
        return getPacket();
    }
@Override
public void handlePacketData(NetworkManager network, String channel, ByteArrayDataInput data) {
	try
        {
            this.angle = data.readInt();
            this.type = data.readInt();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
}
}

 

PacketManager

public class PacketManager implements IPacketHandler
{
//thanks to DarkGuardsman 
@Override
public void onPacketData(NetworkManager network, Packet250CustomPayload packet, Player player)
{
	try
        {
		ByteArrayDataInput data = ByteStreams.newDataInput(packet.data);
		int x = data.readInt();
		int y = data.readInt();
		int z = data.readInt();
                        //the following two are not used but can be for sorting packets by ID and restricting packet reading by lengths
		int id = data.readInt();//packet ID your welcome to set to zero 
		int l = data.readInt(); //packet length a safety var to make sure only data is read.
		EntityPlayer ePlayer = (EntityPlayer) player;
		if(ePlayer != null)
		{
			TileEntity tileEntity = ePlayer.worldObj.getBlockTileEntity(x, y, z);

			if(tileEntity != null)
			{
				if(tileEntity instanceof IPacketReceiver)
				{
					((IPacketReceiver) tileEntity). handlePacketData(network,packet.channel,
							data);

				}
			}
		}
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
}
/**
 * 
 * @param sender - TileEntity sending this packet
 * @param channelName - channel name "channelName"
 * @param id - packet id 
 * @param sendData - list of Integers to be sent is read after main data
 * @param string - list of strings to be sent is read last
 * @return a constructed packet ready to be sent
 */
     //If your looking for a better version check out Calclavia's PacketManager his is very well developed
public static Packet TECommonPacket(TileEntity sender, String channelName,int id, int[] sendData)
    {
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        DataOutputStream data = new DataOutputStream(bytes);

        try
        {
            data.writeInt(sender.xCoord);
            data.writeInt(sender.yCoord);
            data.writeInt(sender.zCoord);
            data.writeInt(id);
            data.writeInt(3+sendData.length);
           //Here we writes our data;
            for(int i =0; i < sendData.length; i++)
            {
                data.writeInt(sendData[i]);
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }

        Packet250CustomPayload packet = new Packet250CustomPayload();
        packet.channel = "UltimateExtender";//TODO change to registered channel for you mod
        packet.data = bytes.toByteArray();
        packet.length = packet.data.length;
        packet.isChunkDataPacket = true;
        return packet;
    }
}

 

YAY!

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.