Posted February 22, 20187 yr Hi all, I'm having issues with creating painting-like entities. Basically I'm trying something very close to vanilla paintings, with an entity extending EntityHanging. They seem to spawn properly server-side but they are not visible client-side; from the debugging I've done it seems they are sent to the client and even the added spawn data is read, but the position of the entity stays set at 0/0/0 client-side. Here is my code: Entity : package org.millenaire.common.entity; import java.io.IOException; import java.util.ArrayList; import org.millenaire.common.config.MillConfigValues; import org.millenaire.common.forge.Mill; import org.millenaire.common.item.MillItems; import org.millenaire.common.utilities.BlockItemUtilities; import org.millenaire.common.utilities.MillLog; import org.millenaire.common.utilities.Point; import org.millenaire.common.utilities.WorldUtilities; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufInputStream; import io.netty.buffer.ByteBufOutputStream; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityHanging; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.SoundEvents; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; public class EntityWallDecoration extends EntityHanging implements IEntityAdditionalSpawnData { public static enum EnumWallDecoration { Griffon("Griffon", 16, 16, 0, 0, NORMAN_TAPESTRY), Oiseau("Oiseau", 16, 16, 16, 0, NORMAN_TAPESTRY), CorbeauRenard("CorbeauRenard", 2 * 16, 16, 2 * 16, 0, NORMAN_TAPESTRY), Serment( "Serment", 5 * 16, 3 * 16, 0, 16, NORMAN_TAPESTRY), MortHarold("MortHarold", 4 * 16, 3 * 16, 5 * 16, 16, NORMAN_TAPESTRY), Drakar( "Drakar", 6 * 16, 3 * 16, 9 * 16, 16, NORMAN_TAPESTRY), MontStMichel("MontStMichel", 3 * 16, 2 * 16, 0, 4 * 16, NORMAN_TAPESTRY), Bucherons("Bucherons", 3 * 16, 2 * 16, 3 * 16, 4 * 16, NORMAN_TAPESTRY), Cuisine("Cuisine", 3 * 16, 2 * 16, 6 * 16, 4 * 16, NORMAN_TAPESTRY), Flotte("Flotte", 15 * 16, 3 * 16, 0, 6 * 16, NORMAN_TAPESTRY), Chasse( "Chasse", 6 * 16, 3 * 16, 0, 9 * 16, NORMAN_TAPESTRY), Siege("Siege", 16 * 16, 3 * 16, 0, 12 * 16, NORMAN_TAPESTRY), Ganesh("Ganesh", 2 * 16, 3 * 16, 0, 0, INDIAN_STATUE), Kali("Kali", 2 * 16, 3 * 16, 2 * 16, 0, INDIAN_STATUE), Shiva("Shiva", 2 * 16, 3 * 16, 4 * 16, 0, INDIAN_STATUE), Osiyan("Osiyan", 2 * 16, 3 * 16, 6 * 16, 0, INDIAN_STATUE), Durga("Durga", 2 * 16, 3 * 16, 8 * 16, 0, INDIAN_STATUE), MayanTeal("MayanTeal", 2 * 16, 2 * 16, 0, 3 * 16, MAYAN_STATUE), MayanGold("MayanGold", 2 * 16, 2 * 16, 2 * 16, 3 * 16, MAYAN_STATUE), LargeJesus("LargeJesus", 2 * 16, 3 * 16, 0, 5 * 16, BYZANTINE_ICON_LARGE), LargeVirgin("LargeVirgin", 2 * 16, 3 * 16, 2 * 16, 5 * 16, BYZANTINE_ICON_LARGE), MediumVirgin1("MediumVirgin1", 2 * 16, 2 * 16, 0, 8 * 16, BYZANTINE_ICON_MEDIUM), MediumVirgin2("MediumVirgin2", 2 * 16, 2 * 16, 2 * 16, 8 * 16, BYZANTINE_ICON_MEDIUM), SmallJesus("SmallJesus", 16, 16, 0, 10 * 16, BYZANTINE_ICON_SMALL), SmallVirgin1("SmallVirgin1", 16, 16, 16, 10 * 16, BYZANTINE_ICON_SMALL), SmallVirgin2("SmallVirgin2", 16, 16, 2 * 16, 10 * 16, BYZANTINE_ICON_SMALL), SmallVirgin3("SmallVirgin3", 16, 16, 3 * 16, 10 * 16, BYZANTINE_ICON_SMALL); public static final int maxArtTitleLength = "SkullAndRoses".length(); public final String title; public final int sizeX; public final int sizeY; public final int offsetX; public final int offsetY; public final int type; private EnumWallDecoration(final String s1, final int j, final int k, final int l, final int i1, final int type) { title = s1; sizeX = j; sizeY = k; offsetX = l; offsetY = i1; this.type = type; } } public static final ResourceLocation WALL_DECORATION = new ResourceLocation(Mill.MODID, "WallDecoration"); public static final int NORMAN_TAPESTRY = 1; public static final int INDIAN_STATUE = 2; public static final int MAYAN_STATUE = 3; public static final int BYZANTINE_ICON_SMALL = 4; public static final int BYZANTINE_ICON_MEDIUM = 5; public static final int BYZANTINE_ICON_LARGE = 6; public static EntityWallDecoration createTapestry(final World world, Point p, final int type) { final EnumFacing orientation = guessOrientation(world, p); if (orientation == EnumFacing.WEST) { p = p.getEast(); } else if (orientation == EnumFacing.SOUTH) { p = p.getNorth(); } else if (orientation == EnumFacing.EAST) { p = p.getWest(); } else if (orientation == EnumFacing.NORTH) { p = p.getSouth(); } return new EntityWallDecoration(world, p.getBlockPos(), orientation, type, true); } private static EnumFacing guessOrientation(final World world, final Point p) { if (BlockItemUtilities.isBlockSolid(WorldUtilities.getBlock(world, p.getNorth()))) { return EnumFacing.SOUTH; } else if (BlockItemUtilities.isBlockSolid(WorldUtilities.getBlock(world, p.getSouth()))) { return EnumFacing.NORTH; } else if (BlockItemUtilities.isBlockSolid(WorldUtilities.getBlock(world, p.getEast()))) { return EnumFacing.WEST; } else if (BlockItemUtilities.isBlockSolid(WorldUtilities.getBlock(world, p.getWest()))) { return EnumFacing.EAST; } return EnumFacing.WEST; } public EnumWallDecoration millArt; public int type; public EntityWallDecoration(final World par1World) { super(par1World); } public EntityWallDecoration(final World world, final BlockPos pos, final EnumFacing facing, final int type, final boolean largestPossible) { super(world, pos); this.type = type; final ArrayList<EnumWallDecoration> arraylist = new ArrayList<EnumWallDecoration>(); int maxSize = 0; for (final EnumWallDecoration enumart : EnumWallDecoration.values()) { if (enumart.type == type) { this.millArt = enumart; this.updateFacingWithBoundingBox(facing); if (onValidSurface()) { if (!largestPossible && ((enumart.sizeX * enumart.sizeY) > maxSize)) { arraylist.clear(); } arraylist.add(enumart); maxSize = enumart.sizeX * enumart.sizeY; } } } if (arraylist.size() > 0) { millArt = arraylist.get(rand.nextInt(arraylist.size())); } if (MillConfigValues.LogBuildingPlan >= MillLog.MAJOR) { MillLog.major(this, "Creating wall decoration: " + pos + "/" + facing + "/" + type + "/" + largestPossible + ". Result: " + millArt.title + " picked amoung " + arraylist.size()); } this.updateFacingWithBoundingBox(facing); } @SideOnly(Side.CLIENT) public EntityWallDecoration(final World world, final int type) {// NO_UCD (instantiated by Forge) this(world); this.type = type; } /** * Get the item to drop */ public Item getDropItem() { if (type == NORMAN_TAPESTRY) { return MillItems.TAPESTRY; } else if (type == INDIAN_STATUE) { return MillItems.INDIAN_STATUE; } else if (type == MAYAN_STATUE) { return MillItems.MAYAN_STATUE; } else if (type == BYZANTINE_ICON_SMALL) { return MillItems.BYZANTINE_ICON_SMALL; } else if (type == BYZANTINE_ICON_MEDIUM) { return MillItems.BYZANTINE_ICON_MEDIUM; } else if (type == BYZANTINE_ICON_LARGE) { return MillItems.BYZANTINE_ICON_LARGE; } MillLog.error(this, "Unknown walldecoration type: "+type); return null; } @Override public int getHeightPixels() { return this.millArt.sizeY; } @Override public int getWidthPixels() { return this.millArt.sizeX; } /** * Called when this entity is broken. Entity parameter may be null. */ @Override public void onBroken(final Entity brokenEntity) { if (this.world.getGameRules().getBoolean("doEntityDrops")) { this.playSound(SoundEvents.ENTITY_PAINTING_BREAK, 1.0F, 1.0F); if (brokenEntity instanceof EntityPlayer) { EntityPlayer entityplayer = (EntityPlayer)brokenEntity; if (entityplayer.capabilities.isCreativeMode) { return; } } this.entityDropItem(new ItemStack(getDropItem()), 0.0F); } } @Override public void playPlaceSound() { this.playSound(SoundEvents.ENTITY_PAINTING_PLACE, 1.0F, 1.0F); } @Override public void onUpdate() { super.onUpdate(); } @Override public boolean onValidSurface() { return super.onValidSurface(); } @Override public void readEntityFromNBT(final NBTTagCompound nbttagcompound) { type = nbttagcompound.getInteger("Type"); final String s = nbttagcompound.getString("Motive"); for (final EnumWallDecoration enumart : EnumWallDecoration.values()) { if (enumart.title.equals(s)) { millArt = enumart; } } if (millArt == null) { millArt = EnumWallDecoration.Griffon; } if (type == 0) { type = NORMAN_TAPESTRY; } super.readEntityFromNBT(nbttagcompound); } @Override public void readSpawnData(final ByteBuf ds) { final ByteBufInputStream data = new ByteBufInputStream(ds); try { type = data.readByte(); final String title = data.readUTF(); for (final EnumWallDecoration enumart : EnumWallDecoration.values()) { if (enumart.title.equals(title)) { millArt = enumart; } } data.close(); } catch (final IOException e) { MillLog.printException("Exception for tapestry " + this, e); } } @Override public String toString() { return "Tapestry (" + millArt.title + ") "+super.toString(); } /** * Set the position and rotation values directly without any clamping. */ @SideOnly(Side.CLIENT) public void setPositionAndRotationDirect(double x, double y, double z, float yaw, float pitch, int posRotationIncrements, boolean teleport) { BlockPos blockpos = this.hangingPosition.add(x - this.posX, y - this.posY, z - this.posZ); this.setPosition((double)blockpos.getX(), (double)blockpos.getY(), (double)blockpos.getZ()); } /** * Sets the location and Yaw/Pitch of an entity in the world */ public void setLocationAndAngles(double x, double y, double z, float yaw, float pitch) { this.setPosition(x, y, z); } @Override public void writeEntityToNBT(final NBTTagCompound nbttagcompound) { nbttagcompound.setInteger("Type", type); nbttagcompound.setString("Motive", this.millArt.title); super.writeEntityToNBT(nbttagcompound); } @Override public void writeSpawnData(final ByteBuf ds) { final ByteBufOutputStream data = new ByteBufOutputStream(ds); try { data.write(type); data.writeUTF(millArt.title); } catch (final IOException e) { MillLog.printException("Exception for painting " + this, e); } finally { try { data.close(); } catch (IOException e) { MillLog.printException("Exception for painting " + this, e); } } } } Item spawning them: package org.millenaire.common.item; import org.millenaire.common.entity.EntityWallDecoration; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumActionResult; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; public class ItemWallDecoration extends ItemMill { public int type; public ItemWallDecoration(final String itemName, final int type) { super(itemName); this.type = type; } @Override public EnumActionResult onItemUse(EntityPlayer player, World worldIn, BlockPos pos, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ) { ItemStack itemstack = player.getHeldItem(hand); BlockPos blockpos = pos.offset(facing); if (facing != EnumFacing.DOWN && facing != EnumFacing.UP && player.canPlayerEdit(blockpos, facing, itemstack)) { final EntityWallDecoration entityhanging = new EntityWallDecoration(worldIn, blockpos, facing, type, false); if (entityhanging != null && entityhanging.onValidSurface()) { if (!worldIn.isRemote) { entityhanging.playPlaceSound(); worldIn.spawnEntity(entityhanging); } itemstack.shrink(1); } return EnumActionResult.SUCCESS; } else { return EnumActionResult.FAIL; } } } Renderer: (whose factory is called when Minecraft initialise, but doRender never) package org.millenaire.client.render; import org.millenaire.common.entity.EntityWallDecoration; import org.millenaire.common.entity.EntityWallDecoration.EnumWallDecoration; import org.millenaire.common.forge.Mill; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.OpenGlHelper; import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.entity.Render; import net.minecraft.client.renderer.entity.RenderManager; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraftforge.fml.client.registry.IRenderFactory; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; /** * Basically a copy of RenderPainting * * Uses one texture for the Norman tapestries and a shared one for the statues and byzantine icons */ @SideOnly(Side.CLIENT) public class RenderWallDecoration extends Render<EntityWallDecoration> { public static final FactoryRenderWallDecoration FACTORY_WALL_DECORATION = new FactoryRenderWallDecoration(); public static final ResourceLocation textureTapestries = new ResourceLocation(Mill.MODID, "textures/painting/tapestry.png"); public static final ResourceLocation textureSculptures = new ResourceLocation(Mill.MODID, "textures/painting/sculptures.png"); public static class FactoryRenderWallDecoration implements IRenderFactory<EntityWallDecoration> { @Override public Render<? super EntityWallDecoration> createRenderFor(final RenderManager manager) { return new RenderWallDecoration(manager); } } protected RenderWallDecoration(final RenderManager renderManager) { super(renderManager); } /** * Renders the desired {@code T} type Entity. */ @Override public void doRender(final EntityWallDecoration entity, final double x, final double y, final double z, final float entityYaw, final float partialTicks) { GlStateManager.pushMatrix(); GlStateManager.translate(x, y, z); GlStateManager.rotate(180.0F - entityYaw, 0.0F, 1.0F, 0.0F); GlStateManager.enableRescaleNormal(); this.bindEntityTexture(entity); final EnumWallDecoration enumart = entity.millArt; GlStateManager.scale(0.0625F, 0.0625F, 0.0625F); if (this.renderOutlines) { GlStateManager.enableColorMaterial(); GlStateManager.enableOutlineMode(this.getTeamColor(entity)); } this.renderPainting(entity, enumart.sizeX, enumart.sizeY, enumart.offsetX, enumart.offsetY); if (this.renderOutlines) { GlStateManager.disableOutlineMode(); GlStateManager.disableColorMaterial(); } GlStateManager.disableRescaleNormal(); GlStateManager.popMatrix(); super.doRender(entity, x, y, z, entityYaw, partialTicks); } @Override protected ResourceLocation getEntityTexture(final EntityWallDecoration entity) { if (entity.type == EntityWallDecoration.NORMAN_TAPESTRY) { return textureTapestries; } else { return textureSculptures; } } /** * Same as the one in RenderPainting in 1.12, with unused variables removed */ private void renderPainting(final EntityWallDecoration painting, final int width, final int height, final int textureU, final int textureV) { float f = (float)(-width) / 2.0F; float f1 = (float)(-height) / 2.0F; for (int i = 0; i < width / 16; ++i) { for (int j = 0; j < height / 16; ++j) { float f15 = f + (float)((i + 1) * 16); float f16 = f + (float)(i * 16); float f17 = f1 + (float)((j + 1) * 16); float f18 = f1 + (float)(j * 16); this.setLightmap(painting, (f15 + f16) / 2.0F, (f17 + f18) / 2.0F); float f19 = (float)(textureU + width - i * 16) / 256.0F; float f20 = (float)(textureU + width - (i + 1) * 16) / 256.0F; float f21 = (float)(textureV + height - j * 16) / 256.0F; float f22 = (float)(textureV + height - (j + 1) * 16) / 256.0F; Tessellator tessellator = Tessellator.getInstance(); BufferBuilder bufferbuilder = tessellator.getBuffer(); bufferbuilder.begin(7, DefaultVertexFormats.POSITION_TEX_NORMAL); bufferbuilder.pos((double)f15, (double)f18, -0.5D).tex((double)f20, (double)f21).normal(0.0F, 0.0F, -1.0F).endVertex(); bufferbuilder.pos((double)f16, (double)f18, -0.5D).tex((double)f19, (double)f21).normal(0.0F, 0.0F, -1.0F).endVertex(); bufferbuilder.pos((double)f16, (double)f17, -0.5D).tex((double)f19, (double)f22).normal(0.0F, 0.0F, -1.0F).endVertex(); bufferbuilder.pos((double)f15, (double)f17, -0.5D).tex((double)f20, (double)f22).normal(0.0F, 0.0F, -1.0F).endVertex(); bufferbuilder.pos((double)f15, (double)f17, 0.5D).tex(0.75D, 0.0D).normal(0.0F, 0.0F, 1.0F).endVertex(); bufferbuilder.pos((double)f16, (double)f17, 0.5D).tex(0.8125D, 0.0D).normal(0.0F, 0.0F, 1.0F).endVertex(); bufferbuilder.pos((double)f16, (double)f18, 0.5D).tex(0.8125D, 0.0625D).normal(0.0F, 0.0F, 1.0F).endVertex(); bufferbuilder.pos((double)f15, (double)f18, 0.5D).tex(0.75D, 0.0625D).normal(0.0F, 0.0F, 1.0F).endVertex(); bufferbuilder.pos((double)f15, (double)f17, -0.5D).tex(0.75D, 0.001953125D).normal(0.0F, 1.0F, 0.0F).endVertex(); bufferbuilder.pos((double)f16, (double)f17, -0.5D).tex(0.8125D, 0.001953125D).normal(0.0F, 1.0F, 0.0F).endVertex(); bufferbuilder.pos((double)f16, (double)f17, 0.5D).tex(0.8125D, 0.001953125D).normal(0.0F, 1.0F, 0.0F).endVertex(); bufferbuilder.pos((double)f15, (double)f17, 0.5D).tex(0.75D, 0.001953125D).normal(0.0F, 1.0F, 0.0F).endVertex(); bufferbuilder.pos((double)f15, (double)f18, 0.5D).tex(0.75D, 0.001953125D).normal(0.0F, -1.0F, 0.0F).endVertex(); bufferbuilder.pos((double)f16, (double)f18, 0.5D).tex(0.8125D, 0.001953125D).normal(0.0F, -1.0F, 0.0F).endVertex(); bufferbuilder.pos((double)f16, (double)f18, -0.5D).tex(0.8125D, 0.001953125D).normal(0.0F, -1.0F, 0.0F).endVertex(); bufferbuilder.pos((double)f15, (double)f18, -0.5D).tex(0.75D, 0.001953125D).normal(0.0F, -1.0F, 0.0F).endVertex(); bufferbuilder.pos((double)f15, (double)f17, 0.5D).tex(0.751953125D, 0.0D).normal(-1.0F, 0.0F, 0.0F).endVertex(); bufferbuilder.pos((double)f15, (double)f18, 0.5D).tex(0.751953125D, 0.0625D).normal(-1.0F, 0.0F, 0.0F).endVertex(); bufferbuilder.pos((double)f15, (double)f18, -0.5D).tex(0.751953125D, 0.0625D).normal(-1.0F, 0.0F, 0.0F).endVertex(); bufferbuilder.pos((double)f15, (double)f17, -0.5D).tex(0.751953125D, 0.0D).normal(-1.0F, 0.0F, 0.0F).endVertex(); bufferbuilder.pos((double)f16, (double)f17, -0.5D).tex(0.751953125D, 0.0D).normal(1.0F, 0.0F, 0.0F).endVertex(); bufferbuilder.pos((double)f16, (double)f18, -0.5D).tex(0.751953125D, 0.0625D).normal(1.0F, 0.0F, 0.0F).endVertex(); bufferbuilder.pos((double)f16, (double)f18, 0.5D).tex(0.751953125D, 0.0625D).normal(1.0F, 0.0F, 0.0F).endVertex(); bufferbuilder.pos((double)f16, (double)f17, 0.5D).tex(0.751953125D, 0.0D).normal(1.0F, 0.0F, 0.0F).endVertex(); tessellator.draw(); } } } /** * Same as the one in RenderPainting in 1.12 */ private void setLightmap(final EntityWallDecoration painting, final float p_77008_2_, final float p_77008_3_) { int i = MathHelper.floor(painting.posX); int j = MathHelper.floor(painting.posY + (double)(p_77008_3_ / 16.0F)); int k = MathHelper.floor(painting.posZ); EnumFacing enumfacing = painting.facingDirection; if (enumfacing == EnumFacing.NORTH) { i = MathHelper.floor(painting.posX + (double)(p_77008_2_ / 16.0F)); } if (enumfacing == EnumFacing.WEST) { k = MathHelper.floor(painting.posZ - (double)(p_77008_2_ / 16.0F)); } if (enumfacing == EnumFacing.SOUTH) { i = MathHelper.floor(painting.posX - (double)(p_77008_2_ / 16.0F)); } if (enumfacing == EnumFacing.EAST) { k = MathHelper.floor(painting.posZ + (double)(p_77008_2_ / 16.0F)); } int l = this.renderManager.world.getCombinedLight(new BlockPos(i, j, k), 0); int i1 = l % 65536; int j1 = l / 65536; OpenGlHelper.setLightmapTextureCoords(OpenGlHelper.lightmapTexUnit, (float)i1, (float)j1); GlStateManager.color(1.0F, 1.0F, 1.0F); } } Model renderer declaration: @SubscribeEvent public static void registerModels(final ModelRegistryEvent event) { MillItems.registerItemModels(); MillBlocks.registerItemBlockModels(); RenderingRegistry.registerEntityRenderingHandler(EntityGenericMale.class, RenderMillVillager.FACTORY_MALE); RenderingRegistry.registerEntityRenderingHandler(EntityGenericAsymmFemale.class, RenderMillVillager.FACTORY_FEMALE_ASYM); RenderingRegistry.registerEntityRenderingHandler(EntityGenericSymmFemale.class, RenderMillVillager.FACTORY_FEMALE_SYM); RenderingRegistry.registerEntityRenderingHandler(EntityWallDecoration.class, RenderWallDecoration.FACTORY_WALL_DECORATION); } And entity declaration in init: EntityRegistry.registerModEntity(EntityWallDecoration.WALL_DECORATION, EntityWallDecoration.class, EntityWallDecoration.WALL_DECORATION.getResourcePath(), id++, MODID, 64, 3, false); I'm skipping the item's declarations etc as the item itself works fine. And debugger when the entity is spawned client-side: Anybody has any idea? I feel like I must be missing something small, probably a mistake in how my entity is declared or something of that kind... Thanks! http://www.millenaire.org/img/dynamicsig.png[/img]
February 23, 20187 yr Author I knew it must be something obvious... Thanks a lot! I'll test that tonight. No idea how it was in 1.7 where I did not have that issue, weird. http://www.millenaire.org/img/dynamicsig.png[/img]
February 23, 20187 yr Author I had to update that class so much for 1.12 I must have removed the equivalent mechanism by mistake. Well, if it's such a simple fix didn't matter. What annoys me is that I had carefully checked the position of the entity for that kind of issues and forgot the facing... http://www.millenaire.org/img/dynamicsig.png[/img]
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.