Jump to content

Recommended Posts

Posted

Can anyone help me? I'm trying to make it, when the progress is more than power_needed, it places output in the 5th slot and removes 1 item from the first 4. BUT it sometimes outputs, or removes the items too early, or even does output nothing/2 items at once/doubles the itemstack already there. WTF is going on???

 

I think it is something with the client not syncing with server ¯\_(ツ)_/¯

 

TileEntityManualMill:

package com.villfuk02.essence.tileentities;

import org.xml.sax.InputSource;

import com.villfuk02.essence.init.ModItems;

import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.init.Items;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.network.Packet;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ITickable;
import net.minecraft.util.math.MathHelper;

public class TileEntityManualMill extends TileEntity implements IInventory, ITickable {

public static final int fuel_slots = 0;
public static final int input_slots = 4;
public static final int output_slots = 1;
public static final int total_slots = input_slots + output_slots + fuel_slots;

public static final int first_fuel_slot = 0;
public static final int first_input_slot = first_fuel_slot + fuel_slots;
public static final int first_output_slot = first_input_slot + input_slots;

private static int recipe_count = 3;

public boolean powered = false;

private ItemStack[] itemStacks = new ItemStack[total_slots];
private String customName;
private static ItemStack[] inputs = {new ItemStack(Items.FEATHER),new ItemStack(Items.STRING),new ItemStack(Items.GLOWSTONE_DUST),new ItemStack(Items.PAPER),new ItemStack(Blocks.LEAVES),new ItemStack(Blocks.TALLGRASS),new ItemStack(Items.WHEAT),new ItemStack(Items.MELON),new ItemStack(Blocks.LEAVES2),new ItemStack(Blocks.TALLGRASS),new ItemStack(Items.WHEAT),new ItemStack(Items.MELON)};
private static ItemStack[] outputs = {new ItemStack(ModItems.essence_dust, 1, 0),new ItemStack(ModItems.essence_dust, 1, 1),new ItemStack(ModItems.essence_dust, 1, 1)};

private int speed;
private int cache_speed = -1;
private int max_speed = 80;
private int power_needed = 16000;	
private int progress;
private int rotation;
private int rotation_stage;

private int max_essence = 1000;
private int essence;
private int essence_type;


public int getSpeed(){
	return rotation;
}

public boolean getPowered(){
	return powered;
}

public double fractionProgress(){
	double fraction = ((double)progress / (double)power_needed);
	return MathHelper.clamp_double(fraction, 0.0, 1.0);
}

public int getRotationStage() {
	return rotation_stage;
}


@Override
public void update(){		
	if(powered){
		speed++;
	}else if(speed > 0){
		speed -=2;
	}
	if (speed > max_speed)
		speed = max_speed;
	if (speed < 0)
		speed = 0;
	if(validRecipe()){
		if(speed > 0){
			rotation += speed;
			progress += speed;
		} else{
			progress -= 20;
		}

		if (progress <0)
			progress = 0;
		if (progress >= power_needed){
			finishRecipe();
			progress = 0;
			markDirty();
		}

		rotation_stage = ((int)((double)rotation / 37.5)) % 4;

		if (rotation > 149)
			rotation -= 150;			

	}else {
		progress = 0;
	}
	powered = false;
}

private boolean validRecipe(){
	ItemStack result = null;
	boolean valid = false;
	result = getOutput(itemStacks[0],itemStacks[1],itemStacks[2],itemStacks[3]);
	if (result != null) {
		ItemStack outputStack = itemStacks[4];
		if (outputStack == null){
			valid = true;				
		}else if (outputStack.getItem() == result.getItem() && outputStack.getMetadata() == result.getMetadata() && ItemStack.areItemStackTagsEqual(outputStack, result)){
			int combinedSize = outputStack.stackSize + result.stackSize;
			if (combinedSize <= 64 && combinedSize <= outputStack.getMaxStackSize()){
				valid = true;
			}
		}
	}
	markDirty();
	return valid;
}

private void finishRecipe(){

	for(int i = 0; i < 4; i++){
		itemStacks[i].stackSize--;
		if (itemStacks[i].stackSize <= 0){
			itemStacks[i] = null;
		}
	}
	if (itemStacks[4] == null){
		itemStacks[4] = getOutput(itemStacks[0],itemStacks[1],itemStacks[2],itemStacks[3]);
	}else{
		itemStacks[4].stackSize += getOutput(itemStacks[0],itemStacks[1],itemStacks[2],itemStacks[3]).stackSize;
	}
	markDirty();
}

public static ItemStack getOutput(ItemStack in1, ItemStack in2, ItemStack in3, ItemStack in4){
	ItemStack result = null;
	ItemStack[] in = {in1, in2, in3, in4};
	if (in1 == null || in2 == null || in3 == null || in4 == null){
		return null;
	}else{
		for(int x = 0; x < recipe_count; x++){
			for (int a = 0; a < 4; a++){
				if (in[a].getItem() == inputs[4*x].getItem()){
					for (int b = 0; b < 4; b++){
						if (in[b].getItem() == inputs[4*x + 1].getItem()){
							for (int c = 0; c < 4; c++){
								if (in[c].getItem() == inputs[4*x + 2].getItem()){
									for (int d = 0; d < 4; d++){
										if (in[d].getItem() == inputs[4*x + 3].getItem()){
											result = outputs[x];
										}				

									}
								}				

							}
						}				

					}
				}				

			}
		}
		if (in1.getItem() == ModItems.essence_dust && in2.getItem() == ModItems.essence_dust && in3.getItem() == ModItems.essence_dust && in4.getItem() == ModItems.essence_dust){
			int data = in1.getMetadata();
			if (in2.getMetadata() == data && in3.getMetadata() == data && in4.getMetadata() == data)
				result = new ItemStack(ModItems.essence_dust, 3, data);
		}
	}

	return result;

}


public String getCustomName(){
	return this.customName;
}

public void setCustomName(String customName){
	this.customName = customName;
}



@Override
public String getName() {
	return this.hasCustomName() ? this.customName : "Manual Essence Mill";
}

@Override
public boolean hasCustomName() {
	return this.customName != null && !this.customName.equals("");
}

@Override
public int getSizeInventory() {
	return 5;
}

@Override
public ItemStack getStackInSlot(int index) {
	if(index < 0 || index >= this.getSizeInventory()){
		return null;
	}
	return this.itemStacks[index];
}

@Override
public ItemStack decrStackSize(int index, int count) {
		if(this.getStackInSlot(index)!= null){
			ItemStack itemStack;

			if (this.getStackInSlot(index).stackSize <= count) {
				itemStack = this.getStackInSlot(index);
				this.setInventorySlotContents(index, null);
				this.markDirty();
				return itemStack;
			}else{
				itemStack = this.getStackInSlot(index).splitStack(count);

				if(this.getStackInSlot(index).stackSize <= 0){
					this.setInventorySlotContents(index, null);
				}else{
					this.setInventorySlotContents(index, this.getStackInSlot(index));
				}
				this.markDirty();
				return itemStack;
			}
		} else {
			return null;
		}
}

@Override
public ItemStack removeStackFromSlot(int index) {
	ItemStack stack = this.getStackInSlot(index);
	this.setInventorySlotContents(index, null);
	return stack;
}

@Override
public void setInventorySlotContents(int index, ItemStack stack) {
	if (index < 0 || index >= this.getSizeInventory())

		return;
	if (stack != null && stack.stackSize > this.getInventoryStackLimit())
		stack.stackSize = this.getInventoryStackLimit();

	if (stack != null && stack.stackSize == 0)
		stack = null;

	this.itemStacks[index] = stack;
	this.markDirty();

}

@Override
public int getInventoryStackLimit() {
	return 64;
}

@Override
public boolean isUseableByPlayer(EntityPlayer player) {
	return this.worldObj.getTileEntity(this.getPos()) == this && player.getDistanceSq(this.pos.add(0.5, 0.5, 0.5)) <= 64;
}

@Override
public void openInventory(EntityPlayer player) {
}

@Override
public void closeInventory(EntityPlayer player) {
}

@Override
public boolean isItemValidForSlot(int index, ItemStack stack) {
	return true;	
}

@Override
public int getField(int id) {
	int value = 0;
	if (id == 0)
		value = progress;
	if (id == 1)
		value = speed;
	if (id == 2)
		value = rotation;
	if (id == 3)
		value = essence;
	if (id == 4)
		value = essence_type;
	return value;
}

@Override
public void setField(int id, int value) {
	if (id == 0)
		progress = value;
	if (id == 1)
		speed = value;
	if (id == 2)
		rotation = value;
	if (id == 3)
		essence = value;
	if (id == 4)
		essence_type = value;		
}

@Override
public int getFieldCount() {
	return 5;
}

@Override
public void clear() {
	for(int i = 0; i < this.getSizeInventory(); i++){
		this.setInventorySlotContents(i, null);
	}
}



@Override
public NBTTagCompound writeToNBT(NBTTagCompound nbt){
	super.writeToNBT(nbt);

	NBTTagList list = new NBTTagList();
	for (int i = 0; i < this.getSizeInventory(); i++){
		if (this.getStackInSlot(i) != null){
			NBTTagCompound stackTag = new NBTTagCompound();
			stackTag.setByte("Slot", ((byte)i));
			this.getStackInSlot(i).writeToNBT(stackTag);
			list.appendTag(stackTag);
		}
	}
	nbt.setTag("Items", list);
	nbt.setInteger("progress", progress);
	nbt.setInteger("speed", speed);
	nbt.setInteger("rotation", rotation);

	if (this.hasCustomName()){
		nbt.setString("CustomName",this.getCustomName());
	}
	return nbt;
}

@Override
public void readFromNBT(NBTTagCompound nbt){
	super.readFromNBT(nbt);

	NBTTagList list = nbt.getTagList("Items", 10);
	for(int i = 0; i < list.tagCount(); ++i){
		NBTTagCompound stackTag = list.getCompoundTagAt(i);
		int slot = stackTag.getByte("Slot") & 255;
		this.setInventorySlotContents(slot, ItemStack.loadItemStackFromNBT(stackTag));

	}

	progress = nbt.getInteger("progress");
	speed = nbt.getInteger("speed");
	rotation = nbt.getInteger("rotation");

	if(nbt.hasKey(customName, ){
		this.setCustomName(nbt.getString("CustomName"));

	}
}




}

 

 

ContainerManualMill:

package com.villfuk02.essence.gui;

import com.villfuk02.essence.tileentities.TileEntityManualMill;

import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.inventory.Container;
import net.minecraft.inventory.IContainerListener;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.Slot;
import net.minecraft.item.ItemStack;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;

public class ContainerManualMill extends Container{

private TileEntityManualMill te;

private int [] cachedFields;

private final int hotbar = 9;
private final int inventory_rows = 3;
private final int inventory_columns = 9;
private final int inventory = inventory_rows * inventory_columns;	
private final int player_slots = hotbar + inventory;

public final int input_slots = 4;
public final int output_slots = 1;
public final int machine_slots = input_slots + output_slots;

private final int first_player_index = 0;
private final int first_machine_index = first_player_index;

public ContainerManualMill(InventoryPlayer player, TileEntityManualMill te){
	this.te = te;

	//Hotbar
	for(int x = 0; x < hotbar; x++){
		addSlotToContainer(new Slot(player, x, 8 + 18 * x, 183));
	}
	//Inventory
	for(int y = 0; y < inventory_rows; y++){
		for(int x = 0; x < inventory_columns; x++){
			addSlotToContainer(new Slot(player, hotbar + x + y * inventory_columns, 8 + 18 * x, 125 + y * 18));
		}
	}
	//Machine
	for (int x = 0; x < input_slots; x++){
		addSlotToContainer(new Slot(te, first_machine_index + x, 44 + 24 * x, 19));
	}
	addSlotToContainer(new Slot(te, first_machine_index + input_slots, 80, 97));
}


@Override
public boolean canInteractWith(EntityPlayer player) {
	return this.te.isUseableByPlayer(player);
}

@Override
public ItemStack transferStackInSlot (EntityPlayer player, int fromSlot) {
	ItemStack previous = null;
	Slot slot = (Slot)this.inventorySlots.get(fromSlot);

	if(slot != null && slot.getHasStack()){
		ItemStack current = slot.getStack();
		previous = current.copy();

		if(fromSlot < 15){
			if(!this.mergeItemStack(current, 36, 41, true))
				return null;
		} else {
			if (!this.mergeItemStack(current, 0, 36, false))
				return null;
		}


		if (current.stackSize == 0)
			slot.putStack((ItemStack)null);
		else
			slot.onSlotChanged();

		if (current.stackSize == previous.stackSize)
			return null;
		slot.onPickupFromSlot(player, current);
	}
	return previous;
}

@Override
public void detectAndSendChanges(){
	super.detectAndSendChanges();

	boolean haveFieldsChanged = false;
	boolean fieldChanged[] = new boolean[this.te.getFieldCount()];
	if(cachedFields == null){
		cachedFields = new int[this.te.getFieldCount()];
		haveFieldsChanged = true;
	}
	for(int i = 0; i < cachedFields.length; i++){
		if(haveFieldsChanged || cachedFields[i] != this.te.getField(i)){
			cachedFields[i] = this.te.getField(i);
			haveFieldsChanged = true;
		}
	}

	for(int i = 0; i < this.listeners.size(); i++){
		IContainerListener iListener = (IContainerListener)this.listeners.get(i);
		for(int fieldID = 0; fieldID < this.te.getFieldCount(); ++fieldID){
			if(fieldChanged[fieldID]){
				iListener.sendProgressBarUpdate(this, fieldID, cachedFields[fieldID]);
			}
		}
	}

}

@SideOnly(Side.CLIENT)
@Override
public void updateProgressBar(int id, int data){
	this.te.setField(id, data);
}


}

 

GuiManualMill:

package com.villfuk02.essence.gui;

import java.util.ArrayList;
import java.util.List;

import com.villfuk02.essence.client.Reference;
import com.villfuk02.essence.tileentities.TileEntityManualMill;

import io.netty.util.concurrent.ProgressiveFuture;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.inventory.GuiContainer;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.entity.player.InventoryPlayer;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;

@SideOnly(Side.CLIENT)
public class GuiManualMill extends GuiContainer{

private static final ResourceLocation texture = new ResourceLocation(Reference.MOD_ID + ":" + "textures/gui/container/manual_mill_gui.png");
private TileEntityManualMill entity;
private InventoryPlayer player;

public GuiManualMill(InventoryPlayer player, TileEntityManualMill entity) {
	super(new ContainerManualMill(player, entity));

	xSize = 176;
	ySize = 207;

	this.entity = entity;
	this.player = player;
}

final int progress_bar_xpos = 78;
final int progress_bar_ypos = 38;
final int progress_bar_icon_u = 176;
final int progress_bar_icon_v = 0;
final int progress_bar_width = 20;
final int progress_bar_height = 58;

final int gear_xpos = 80;
final int gear_ypos = 58;
final int gear_icon_u = 0;
final int gear_icon_v = 210;
final int gear_width = 16;
final int gear_height = 16;


@Override
protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY) {
	Minecraft.getMinecraft().getTextureManager().bindTexture(texture);
	GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f);
	drawTexturedModalRect(this.guiLeft, this.guiTop, 0, 0, xSize, ySize);
	double progress = entity.fractionProgress();
	this.drawTexturedModalRect(this.guiLeft + progress_bar_xpos, this.guiTop + progress_bar_ypos, progress_bar_icon_u, progress_bar_icon_v, progress_bar_width, (int)(progress * (double)progress_bar_height));
	int rotate = entity.getRotationStage();
	this.drawTexturedModalRect(this.guiLeft + gear_xpos, this.guiTop + gear_ypos, gear_icon_u + rotate * 16, gear_icon_v, gear_width, gear_height);

}

@Override
protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY){
	super.drawGuiContainerForegroundLayer(mouseX, mouseY);
	String s = this.entity.getName();
	String spd = this.entity.getSpeed() + " RPM";
	this.fontRendererObj.drawString(s, 88 - this.fontRendererObj.getStringWidth(s) / 2, 6, 4210752);
	this.fontRendererObj.drawString(this.player.getDisplayName().getUnformattedText(), 8, 115, 4210752);
	this.fontRendererObj.drawString(spd, 8, 90, 4210752);

	List<String> text = new ArrayList<String>();
	if(isInRect(guiLeft + progress_bar_xpos, guiTop + progress_bar_ypos, progress_bar_width, progress_bar_height, mouseX, mouseY)){
		text.add("Progress: ");
		int percentage = (int)(entity.fractionProgress() * 100.0);
		text.add(percentage + "%");
	}

	if(!text.isEmpty()){
		drawHoveringText(text, mouseX - guiLeft, mouseY - guiTop, fontRendererObj);
	}
}

public static boolean isInRect(int x, int y, int xSize, int ySize,int mouseX, int mouseY){
	return((mouseX >= x && mouseX <= x + xSize)&&(mouseY >= y && mouseY <= y + ySize));
}

}

 

 

BlockManualMill:

package com.villfuk02.essence.blocks;

import java.util.Random;

import com.villfuk02.essence.client.Reference;
import com.villfuk02.essence.gui.GuiHandler;
import com.villfuk02.essence.init.ModBlocks;
import com.villfuk02.essence.tileentities.TileEntityComparingChest;
import com.villfuk02.essence.tileentities.TileEntityManualMill;

import net.minecraft.block.BlockContainer;
import net.minecraft.block.SoundType;
import net.minecraft.block.material.MapColor;
import net.minecraft.block.material.Material;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.InventoryHelper;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;

public class BlockManualMill extends BlockContainer{

public BlockManualMill(String unlocalizedName, String registryName) {
	super(Material.IRON);
	this.setUnlocalizedName(unlocalizedName);
	this.setRegistryName(new ResourceLocation(Reference.MOD_ID, registryName));
	this.setHardness(3.0f);
	this.setResistance(40.0f);
	this.setHarvestLevel("pickaxe", 0);
	this.setSoundType(SoundType.METAL);
}

@Override
public void breakBlock(World world, BlockPos pos, IBlockState state) {
	TileEntityComparingChest te = (TileEntityComparingChest)world.getTileEntity(pos);
	InventoryHelper.spawnItemStack(world, pos.getX(), pos.getY(), pos.getZ(), new ItemStack(Item.getItemFromBlock(ModBlocks.manual_mill), 1, 0, te.writeToNBT(new NBTTagCompound())));
	super.breakBlock(world, pos, state);
}

@Override
public MapColor getMapColor(IBlockState state) {
	return MapColor.STONE;
}

@Override
public TileEntity createNewTileEntity(World worldIn, int meta) {
	return new TileEntityManualMill();
}

@Override
public boolean onBlockActivated(World worldIn, BlockPos pos, IBlockState state, EntityPlayer playerIn, EnumHand hand, ItemStack heldItem, EnumFacing side, float hitX, float hitY, float hitZ) {
	if (worldIn.isRemote){
		return true;
	}
	playerIn.openGui(Reference.MOD_ID, GuiHandler.MANUAL_MILL_GUI, worldIn, pos.getX(), pos.getY(), pos.getZ());
	return true;
}



@Override
public void onBlockPlacedBy(World world, BlockPos pos, IBlockState state, EntityLivingBase placer, ItemStack stack) {
	if (stack.hasDisplayName()){
		((TileEntityManualMill)world.getTileEntity(pos)).setCustomName(stack.getDisplayName());
	}
}

@Override
public boolean isFullCube(IBlockState state) {
	return false;
}

@Override 
public boolean isNormalCube(IBlockState state){
	return false;
}

}

 

THX for any response!

 

Posted

I've also noticed, when i reopen the gui, it resets. I mean like when i put some items in it, it works, but when the changes are made via processing the resources and outputting the result, they dont save.

Posted

I think it is something with the client not syncing with server ¯\_(ツ)_/¯

 

This is possible from the behaviour you described. The client does not know what the server does. This is normal. The DetectAndSendChanges method in the Container should sync the values when the gui is opened. Maybe there is a bug somewhere. But it's a lot of code to look through, I could not see an obvious mistake in a quick look. You could try debugging with System.out.println, there you see what thread has written it to the console. If it's in a code that gets called on both sides you can see possible desyncs.

 

Posted

INTERESTING:

The items are doubling, because server thinks there arent items, so it in finishRecipe() sets both slot and the recipe as the same but for server the slot doesnt change but the recipe changes and its changed even between worlds, just until you close minecraft.

 

So i need to call server to update the slot after finishRecipe()

Posted

You should also stop using IInventory and use IItemHandler instead.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Posted

You should let run your item creating/removing code only on the server. (In tile entity update). On the client you should only change progress. If the server changes something with it's contents or state (finish/start), you need to send out a sync packet. This is done by marking the block for update. When getDescriptionPaket and onDataPaket is implemented in the tileEntity a sync with NBT tags is made.

Posted

You should let run your item creating/removing code only on the server. (In tile entity update).

 

 

BUT HOW?!!?

I am trying two days already and cant figure it out :'(

HOW AM I SUPPOSED TO RUN finishRecipe FROM THE SERVER SIDE??

 

 

PLEASE HELP

Posted

You should also stop using IInventory and use IItemHandler instead.

 

I dont know how does IItemHandler work, can you please show me, what am i supposed to do with the boolean simulate, or the other things?

Posted

I dont know how does IItemHandler work, can you please show me, what am i supposed to do with the boolean simulate, or the other things?

 

Read the javadoc

 

    /**

    * Inserts an ItemStack into the given slot and return the remainder.

    * The ItemStack should not be modified in this function!

    * Note: This behaviour is subtly different from IFluidHandlers.fill()

    *

    * @param slot    Slot to insert into.

    * @param stack    ItemStack to insert.

    * @param simulate If true, the insertion is only simulated

    * @return The remaining ItemStack that was not inserted (if the entire stack is accepted, then return null).

    *        May be the same as the input ItemStack if unchanged, otherwise a new ItemStack.

    **/

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

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.