• Recently Browsing

    No registered users viewing this page.

  • Posts

    • Unfortunately, 1.7.10 is no longer supported. Please update to at least LTS 1.15.2 to receive support.
    • Hello!I'm Japanese Modder but there is little modding information in Japan,so I ask you. I make a chest,that can have many item. But when I open the inventory in Multiplay,The displayed amount in GUI and the amount in the chest are different. For example,The chest has 101,985,303 items,but GUI shows 23 items   TileEntity   public abstract class TileEntityInfinityChestBase extends TileEntity implements ISidedInventory{ /**チェストの中に入っているアイテム*/ private ItemStack chestItem; /**INスロットにあるアイテム*/ private ItemStack inputSlot; /**このチェストのContainer*/ private static ContainerInfinity container=null; public int maxCapacity; public TileEntityInfinityChestBase(){ } /**Containerを設定*/ public void setContainer(ContainerInfinity cont){ container=cont; } @Override public void markDirty(){ if(isMax()){ //満杯なら、INスロットにアイテムを表示させる this.inputSlot=getCopy(this.chestItem.getMaxStackSize()); } //else if((this.chestItem!=null)&&this.maxCapacity-this.chestItem.stackSize<64) { //this.inputSlot=getCopy(this.chestItem.stackSize-this.maxCapacity+64); // } else{ //満杯でなければ何も表示しない this.inputSlot=null; } if(container!=null){ //コンテナのスロットにアイテムを設定する container.changeSlot(); } List<EntityPlayer> list=this.worldObj.playerEntities; for(EntityPlayer player:list){ if((player instanceof EntityPlayerMP)){ ((EntityPlayerMP)player).playerNetServerHandler.sendPacket(getDescriptionPacket()); } } } @Override public void updateEntity(){ } /**チェストのアイテムを一個コピーして返す*/ public ItemStack getCopy(){ return getCopy(1); } /**チェストアイテムを引数の数だけコピーして返す*/ public ItemStack getCopy(int size){ if(!hasStack()){ return null; } ItemStack stack=this.chestItem.copy(); stack.stackSize=size; return stack; } /**チェスト内のアイテムを取得する*/ public ItemStack getStack(){ return this.chestItem; } /**チェスト内のアイテムを表示欄に設定する*/ public void setStack(ItemStack stack){ setStack(stack,false); } /**チェスト内のアイテムを表示欄に設定する*/ public void setStack(ItemStack stack,boolean isBlockSet){ if(isBlockSet){ this.chestItem=stack; this.markDirty(); }else{ this.setInventorySlotContents(0,stack); } } /**チェストにアイテムが入っているか*/ public boolean hasStack(){ if((this.chestItem!=null)&&(this.chestItem.stackSize<=0)){ this.chestItem=null; } return this.chestItem!=null; } /**チェストが満杯かどうか*/ public boolean isMax(){ return (this.chestItem!=null)&&(this.chestItem.stackSize>=this.maxCapacity); } /**渡されたItemStackとチェスト内のアイテムが同じかどうかを返す*/ public boolean isItemEqual(ItemStack stack){ return (this.chestItem!=null)&&(isStackable(stack)) &&(stack.isItemEqual(this.chestItem)); } /**渡されたItemStackがスタック可能かを返す*/ public boolean isStackable(ItemStack stack){ return (stack!=null)&&(!stack.hasTagCompound())&&((!stack.isItemDamaged())||(stack.getItemDamage()==0)); } public ItemStack decSize(int size){ return decrStackSize(0,size); } /**チェスト内のアイテムを引数分追加し、その後のアイテムスタックを返す*/ public ItemStack addSize(int size){ if(!this.hasStack()){ return null; } ItemStack result=null; if(this.chestItem.stackSize+size>this.maxCapacity){ //チェスト内のアイテムの数と足して溢れるなら result=this.chestItem.copy(); result.stackSize=(this.maxCapacity-this.chestItem.stackSize); //余った分を計算する this.chestItem.stackSize=this.maxCapacity; //満杯にする }else{ this.chestItem.stackSize+=size; } markDirty(); return result; } /**チェスト内のアイテムに渡されたItemStack分を追加し、余った分を返す*/ public int addStack(ItemStack itemstack){ if(itemstack==null){ return 0; } int size=itemstack.stackSize; if(!hasStack()){ //何も入っていなければ this.chestItem=itemstack.copy(); //アイテムをセット if(size>this.maxCapacity){ this.chestItem.stackSize=this.maxCapacity; } size-=this.chestItem.stackSize; return size; } if(!isItemEqual(itemstack)){ //渡されたアイテムスタックと内容物が違うなら元のサイズをそのまま返す return size; } if(this.chestItem.stackSize+size>this.maxCapacity){ //内容物と元のサイズを足して余るようなら size=this.chestItem.stackSize+size-this.maxCapacity; //余った分を計算 this.chestItem.stackSize=this.maxCapacity; }else{ this.chestItem.stackSize+=size; size=0; } this.markDirty(); return size; } public int decStack(ItemStack stack){ return decStack(stack,0); } public int decStack(ItemStack stack,int itemLimit){ if((!isItemEqual(stack))||(itemLimit<0)){ return 0; } if(itemLimit==0){ itemLimit=stack.getMaxStackSize(); } if(stack.stackSize>=itemLimit){ return 0; } int size=stack.stackSize; int max=itemLimit-size; if(max>=this.chestItem.stackSize){ size=this.chestItem.stackSize; this.chestItem=null; }else{ size=max; this.chestItem.stackSize-=max; } this.markDirty(); return size; } @Override public int getSizeInventory(){ return 2; } @Override public ItemStack getStackInSlot(int slot){ // if((this.chestItem!=null)&&this.maxCapacity-this.chestItem.stackSize<64) { // ItemStack item=this.getCopy(this.chestItem.stackSize-this.maxCapacity+64); // if(slot==1) //return item; // } return slot==1 ? this.inputSlot : this.chestItem; } @Override /**指定したスロットのアイテムを指定した分だけ減らす*/ public ItemStack decrStackSize(int slot,int dec){ if(!hasStack()){ return null; } if(slot==1){ //搬入スロットだったら何もしない return null; } if(this.chestItem.stackSize<=dec){ //減らす分がチェストの内容量より大きければ ItemStack result=this.chestItem.copy(); this.chestItem=null; //空にする markDirty(); return result; } ItemStack result=this.chestItem.splitStack(dec); markDirty(); return result; } @Override public ItemStack getStackInSlotOnClosing(int slot){ return slot==1 ? this.inputSlot : this.chestItem; } @Override public void setInventorySlotContents(int slot,ItemStack setstack){ if(slot==1){ //搬入スロットにセットする場合 int size=addStack(setstack); //とりあえず追加、余った分を取得 if(size>0){ //余っていれば setstack.stackSize=size; }else{ if(this.chestItem!=null&&this.chestItem.stackSize<this.maxCapacity-64) { this.inputSlot=null; } } }else{ //搬入スロットでなければ this.chestItem=setstack; if(this.chestItem!=null){ if(this.chestItem.stackSize>this.maxCapacity){ //最大容量超過なら this.chestItem.stackSize=this.maxCapacity; //最大容量にする } } } markDirty(); } /**アイテムスタックをチェストの上にドロップさせる*/ public void popItems(ItemStack stack,float height){ float f=this.worldObj.rand.nextFloat()*0.8F+0.1F; float f1=this.worldObj.rand.nextFloat()*0.8F+0.1F; float f2=this.worldObj.rand.nextFloat()*0.8F+0.1F; EntityItem entityitem=new EntityItem(this.worldObj,this.xCoord+f,this.yCoord+f1+0.5F,this.zCoord+f2,stack); float f3=0.05F; entityitem.motionX=((float)this.worldObj.rand.nextGaussian()*f3); entityitem.motionY=((float)this.worldObj.rand.nextGaussian()*f3+height); entityitem.motionZ=((float)this.worldObj.rand.nextGaussian()*f3); this.worldObj.spawnEntityInWorld(entityitem); } @Override public int[] getAccessibleSlotsFromSide(int side){ //上面からは搬入スロットにアクセスできる return new int[]{0,1}; } @Override public boolean canExtractItem(int slot,ItemStack var2,int side){ return slot==0; } @Override public boolean canInsertItem(int slot,ItemStack var2,int side){ return isItemValidForSlot(slot,var2); } @Override public String getInventoryName(){ return ""; } @Override public boolean hasCustomInventoryName(){ return false; } @Override /**指定したスロットに指定したItemStackが入るかを返す*/ public boolean isItemValidForSlot(int slot,ItemStack var2){ return (slot==1)&&((this.chestItem==null) ||((this.chestItem.isItemEqual(var2))&&(ItemStack.areItemStackTagsEqual(this.chestItem,var2)))); } @Override public void readFromNBT(NBTTagCompound nbt){ super.readFromNBT(nbt); if(nbt.getTag("chestItem")!=null){ this.chestItem=readFromNBTCustom((NBTTagCompound)nbt.getTag("chestItem")); }else{ this.chestItem=null; } } @Override public void writeToNBT(NBTTagCompound nbt){ super.writeToNBT(nbt); setNBT(nbt); } /**渡されたNBTに内容物の情報を書き込む*/ public NBTTagCompound setNBT(NBTTagCompound nbt){ NBTTagCompound nbtTagCompound=new NBTTagCompound(); if(this.chestItem!=null){ nbtTagCompound.setShort("id",(short)Item.getIdFromItem(this.chestItem.getItem())); nbtTagCompound.setInteger("Count",this.chestItem.stackSize); nbtTagCompound.setShort("Damage",(short)this.chestItem.getItemDamage()); if(this.chestItem.stackTagCompound!=null){ nbtTagCompound.setTag("tag",this.chestItem.stackTagCompound); } } nbt.setTag("chestItem",nbtTagCompound); return nbt; } /**渡されたNBTに渡されたItemStackの情報を書き込む*/ public static NBTTagCompound setToNBTCustom(NBTTagCompound nbt,ItemStack stack){ NBTTagCompound nbtTagCompound=new NBTTagCompound(); if(stack!=null){ nbtTagCompound.setShort("id",(short)Item.getIdFromItem(stack.getItem())); nbtTagCompound.setInteger("Count",stack.stackSize); nbtTagCompound.setShort("Damage",(short)stack.getItemDamage()); if(stack.stackTagCompound!=null){ nbtTagCompound.setTag("tag",stack.stackTagCompound); } } nbt.setTag("chestItem",nbtTagCompound); return nbt; } /**渡されたNBTから情報を書き出す*/ public static ItemStack readFromNBTCustom(NBTTagCompound nbtTagCompound){ if(nbtTagCompound==null){ return null; } Item item=Item.getItemById(nbtTagCompound.getShort("id")); if(item==null){ return null; } int stackSize=nbtTagCompound.getInteger("Count"); int itemDamage=nbtTagCompound.getShort("Damage"); if(itemDamage<0){ itemDamage=0; } ItemStack itemStack=new ItemStack(item,stackSize,itemDamage); if(nbtTagCompound.hasKey("tag",10)){ NBTTagCompound stackTagCompound=nbtTagCompound.getCompoundTag("tag"); itemStack.setTagCompound(stackTagCompound); } return itemStack; } @Override /**インベントリの最大容量*/ public int getInventoryStackLimit(){ return this.maxCapacity; } @Override /**プレイヤーがこのTileEntityを使えるかどうか*/ public boolean isUseableByPlayer(EntityPlayer par1EntityPlayer){ return (this.worldObj.getTileEntity(this.xCoord,this.yCoord,this.zCoord)==this) ||(par1EntityPlayer.getDistanceSq(this.xCoord+0.5D,this.yCoord+0.5D,this.zCoord+0.5D)<=64.0D); } @Override public void openInventory(){ markDirty(); } @Override public void closeInventory(){ container=null; markDirty(); } @Override public Packet getDescriptionPacket(){ NBTTagCompound nbtTagCompound=new NBTTagCompound(); writeToNBT(nbtTagCompound); return new S35PacketUpdateTileEntity(this.xCoord,this.yCoord,this.zCoord,1,nbtTagCompound); } //パケットの送受信 @Override public void onDataPacket(NetworkManager net,S35PacketUpdateTileEntity pkt){ readFromNBT(pkt.func_148857_g()); } } Container public class ContainerInfinity extends Container{ private TileEntityInfinityChestBase chest; private InventoryPlayer playerInventory; private IInventory guiInventory=new InventoryBasic("slots",false,3); public ContainerInfinity(IInventory plInv, TileEntityInfinityChestBase tileInv){ this.chest = tileInv; tileInv.setContainer(this); tileInv.openInventory(); this.playerInventory = ((InventoryPlayer)plInv); addSlotToContainer(new SlotInfinity(tileInv, 0, 12, 21, false, false)); //ディスプレイ addSlotToContainer(new SlotInfinity(tileInv, 1, 80, 63, true, true)); //INスロット addSlotToContainer(new SlotInfinity(this.guiInventory, 1, 134, 63, true, false)); //OUTスロット changeSlot(); for (int y = 0; y < 3; y++) { for (int x = 0; x < 9; x++) { addSlotToContainer(new Slot(plInv, x + y * 9 + 9, 8 + x * 18, 84 + y * 18)); } } for (int x = 0; x < 9; x++) { addSlotToContainer(new Slot(plInv, x, 8 + x * 18, 142)); } } /**TileEntity内にアイテムがあればディスプレイスロットに設定する * おそらくサーバー側でバグっている部分*/ public void changeSlot(){ if(!this.chest.hasStack()){ this.guiInventory.setInventorySlotContents(1,null); return; } ItemStack chestStack=this.chest.getStack(); int maxStack=chestStack.getMaxStackSize(); ItemStack slot2=chestStack.copy(); System.out.println(slot2.stackSize); if(slot2.stackSize>=maxStack){ slot2.stackSize=maxStack; } this.guiInventory.setInventorySlotContents(1,slot2); } @Override public boolean canInteractWith(EntityPlayer player){ return this.chest.isUseableByPlayer(player); } @Override /**スロットをクリックした際の処理 * slot:スロット番号、mouse:0=左、1=右、2=ミドル、isShift:0=false,1=true*/ public ItemStack slotClick(int slot,int mouse,int isShift,EntityPlayer player){ /*スロット番号 * 0:アイテム表示欄 * 1:搬入スロット * 2:搬出スロット * 3~38:プレイヤーインベントリ*/ if((mouse>1)||(mouse<0)){ //マウスの値が不正だった場合の処理 return null; } if(slot==0){ //アイテム表示欄をクリックした際、デバッグモードを使う return debugmode(mouse,isShift,player); } ItemStack stack=player.inventory.getItemStack(); //今掴んでるアイテム if(stack==null){ //何も掴んでない時 if(slot==1){ //搬入スロットクリック時は何もしない return null; } if(slot==2){ //搬出スロットクリック時 if(isShift!=1){ //shift押していなければ掴む return outPickup(mouse,player.inventory); } //プレイヤーインベントリに追加 return addInventory(player.inventory); } if((isShift!=1)||(slot<0)){ return super.slotClick(slot,mouse,isShift,player); } return addChest(slot,player.inventory); } if(slot==1){ //搬入スロットクリック時 if(isShift!=1){ //シフトを押していなければ return addChest(stack,mouse,player.inventory); } return addChest(stack,player.inventory); //一括移動 } if(slot==2){ //搬出スロットクリック時は何もしない return null; } if((isShift!=1)||(slot<0)){ return super.slotClick(slot,mouse,isShift,player); } return addInventory(stack,player.inventory); } protected ItemStack outPickup(int mouse,InventoryPlayer playerInventory){ ItemStack slotStack=this.guiInventory.getStackInSlot(1); if(slotStack==null){ return null; } int size=slotStack.stackSize; if(mouse==1){ size=(size&0x1)+(size>>1); } playerInventory.setItemStack(slotStack.splitStack(size)); ItemStack result=this.chest.decSize(size); changeSlot(); return result; } /**アイテムをチェストに入れる(掴んでる分だけ)*/ protected ItemStack addChest(ItemStack stack,int mouse,InventoryPlayer playerInventory){ if(!this.chest.isStackable(stack)){ return null; } if(mouse==1){ ItemStack copy=stack.copy(); copy.stackSize=1; if(addChest(copy)==null){ playerInventory.setItemStack(decStack(stack,1)); changeSlot(); } return null; } playerInventory.setItemStack(addChest(stack)); return null; } /**アイテムをチェストに入れる(一括移動)*/ protected ItemStack addChest(ItemStack stack,InventoryPlayer playerInventory){ if(addChest(stack)==null){ playerInventory.setItemStack(null); } for(int i=0;i<36;i++){ Slot cs=(Slot)this.inventorySlots.get(i+3); ItemStack slotStack=cs.getStack(); if((this.chest.isItemEqual(slotStack))&&(addChest(i+3,playerInventory)!=null)){ return null; } } return null; } /**プレイヤーの与えられたスロットのアイテムをチェストに追加する*/ protected ItemStack addChest(int slot,InventoryPlayer playerInventory){ Slot cs=(Slot)this.inventorySlots.get(slot); ItemStack slotStack=cs.getStack(); ItemStack result=null; if(slotStack!=null){ result=addChest(slotStack); } cs.putStack(result); changeSlot(); return result; } protected ItemStack addChest(ItemStack stack){ if(!this.chest.isStackable(stack)){ return stack; } if(!this.chest.hasStack()){ this.chest.setStack(stack); changeSlot(); return null; } if(this.chest.getStack().stackSize>=chest.maxCapacity){ return stack; } if(!this.chest.isItemEqual(stack)){ return stack; } ItemStack result=this.chest.addSize(stack.stackSize); int size=result==null ? stack.stackSize : result.stackSize; ItemStack result2=decStack(stack,size); changeSlot(); return result2; } /**与えられたアイテムスタックを、指定数減らして返す*/ public ItemStack decStack(ItemStack stack,int size){ stack.stackSize-=size; if(stack.stackSize<=0){ stack=null; } return stack; } /**プレイヤーにアイテムを追加する*/ protected ItemStack addInventory(InventoryPlayer playerInventory){ ItemStack stack=this.guiInventory.getStackInSlot(1); //INスロットを確認 if(stack==null){ return null; } int sizeA=stack.stackSize; //INスロットのアイテムの数を取得 ItemStack result=addInventory(stack); int size = result==null?0:result.stackSize; this.chest.decSize(sizeA-size); changeSlot(); return result; } /**与えられたアイテムスタックをプレイヤーに追加する*/ protected ItemStack addInventory(ItemStack stack,InventoryPlayer playerInventory){ if(!this.chest.isItemEqual(stack)){ return null; } this.chest.getStack().stackSize+=stack.stackSize; playerInventory.setItemStack(null); ItemStack result=addInventory(this.chest.getStack()); this.chest.setStack(result); changeSlot(); return result; } protected ItemStack addInventory(ItemStack stack){ if(!this.chest.isItemEqual(stack)){ return stack; } int stacksize=stack.stackSize; //追加するアイテムのスタック数 for(int i=0;i<36;i++){ if(stacksize<=0){ break; } Slot cs=(Slot)this.inventorySlots.get(i+3); ItemStack slotStack=cs.getStack(); if(((slotStack==null)||(this.chest.isItemEqual(slotStack))) &&((slotStack==null)||(slotStack.stackSize!=slotStack.getMaxStackSize()))){ int size; // if(slotStack!=null){ size=stack.getMaxStackSize()-slotStack.stackSize; }else{ size=stack.getMaxStackSize(); } if(size>0){ ItemStack putStack=stack.copy(); putStack.stackSize=(slotStack==null ? 0 : slotStack.stackSize); if(stacksize<size){ size=stacksize; } putStack.stackSize+=size; decStack(stack,size); stacksize-=size; cs.putStack(putStack); if(stacksize<=0){ stacksize=0; break; } } } } stack.stackSize=stacksize; if(stacksize<=0){ stack=null; } return stack; } public ItemStack debugmode(int mouse,int isShift,EntityPlayer player){ if(player.capabilities.isCreativeMode){ if(mouse==0){ this.chest.addSize(isShift==1 ? 100000000 : 64); }else{ this.chest.decSize(isShift==1 ? 100000000 : 64); } changeSlot(); } return null; } @Override public void onContainerClosed(EntityPlayer player){ super.onContainerClosed(player); this.chest.closeInventory(); } } GUI @SideOnly(Side.CLIENT) public class GuiInfinity extends GuiContainer{ private IInventory player; private TileEntityInfinityChestBase tile; private static final ResourceLocation GUI=new ResourceLocation("storagemod","textures/gui/infinity.png"); public static Logger logger = LogManager.getLogger("StorageMod"); public GuiInfinity(IInventory playerInventory, TileEntityInfinityChestBase tileInventory){ super(new ContainerInfinity(playerInventory, tileInventory)); itemRender = new RenderItemInfinity(); this.player = playerInventory; this.tile = tileInventory; this.xSize = 176; this.ySize = 166; } @Override /**GUIの描画(前面)*/ protected void drawGuiContainerForegroundLayer(int p_146979_1_,int p_146979_2_){ this.fontRendererObj.drawString(String.format(StatCollector.translateToLocal(this.tile.getInventoryName()),new Object[]{ClientStringUtils.formatMax(tile.getInventoryStackLimit())}),8,5,4210752); this.fontRendererObj.drawString(StatCollector.translateToLocal(this.player.getInventoryName()),8,72,4210752); if(this.tile.hasStack()){ ItemStack stack=this.tile.getStack(); //何故か数がおかしい String name=stack.getDisplayName(); this.fontRendererObj.drawString(name,35,17,4210752); //アイテムの名前を表示 String count=ClientStringUtils.formatStack(stack.stackSize); this.fontRendererObj.drawString(count,47,29,4210752); //アイテム数を表示 String lc=ClientStringUtils.formatLC(stack.stackSize); this.fontRendererObj.drawString(lc,164-this.fontRendererObj.getStringWidth(lc),40,4210752); //アイテム数のLC換算を表示 ArrayList info=new ArrayList(); stack.getItem().addInformation(stack,ClientStringUtils.mc.thePlayer,info,false); if(info.size()>0){ for(int i=0;i<info.size();i++){ this.fontRendererObj.drawString((String)info.get(i),7,42+i*10,4210752); } } } } @Override /**GUIの描画(後面)*/ protected void drawGuiContainerBackgroundLayer(float par1,int par2,int par3){ GL11.glColor4f(1.0F,1.0F,1.0F,1.0F); this.mc.renderEngine.bindTexture(GUI); int x=(this.width-this.xSize)/2; int y=(this.height-this.ySize)/2; drawTexturedModalRect(x,y,0,0,this.xSize,this.ySize); } } I think the synchronization process doesn't work on the client and server,but true cause isn't clear     Modding environment: Minecraft version:1.7.10 Forge 10.13.4.1558
    • So imagine it like this. Technically, with a packet to synchronize it to the client, the client doesn't know anything about a slot either. What it does is send the container, slot id, and item stack. The container holds the window id (which should be the same on both the client and server version). The slot id holds the current slot it's in (once again synchronized by the client and server each creating their own version). Finally all that's left is the item stack sent. Once it puts all three of these together, it compares the open container window id with the packet version. If equal, it gets the client slot and pushes the stack to it.   Your implementation of this would be similar. Any time you detect a change in the slot, you will send a packet to the client with the container window id and the current fluid stack. If you have more than one fluid, you can handle it similarly to a slot by creating a list and grabbing it from that. If you only have one, you can create a public getter and push the value to yours.   Once you get this working, I will review the other problems you have. It's best to solve one problem at a time.
    • Hello all, I am getting the error: FATAL ERROR, You need to run the installer. The libraries required to launch a server are missing in my console when I try to run forge.   25.09 23:52:07 [Multicraft] Received start command 25.09 23:52:07 [Multicraft] Loading server properties 25.09 23:52:07 [Multicraft] Starting server! 25.09 23:52:07 [Multicraft] Loaded config for "Custom JAR (Place "custom.jar" in base directory)" 25.09 23:52:08 [Multicraft] Updating eula.txt file 25.09 23:52:08 [Server] INFO FATAL ERROR, You need to run the installer. The libraries required to launch a server are missing 25.09 23:52:08 [Multicraft] Server shut down (starting) 25.09 23:52:08 [Multicraft] Restarting crashed server in 10 seconds 25.09 23:52:08 [Multicraft] Looks like a crash, check the server console. Return value: 1 25.09 23:52:08 [Multicraft] Server stopped   The steps I took so far were: Downloaded and ran the forge 1.16.1 installer Put the files and folders the installer created into my server's root folder Renamed forge-1.16.1-32.0.108.jar to custom.jar Ran the server via custom.jar I'm really not sure what the problem is. It's telling me I need to run the installer when I already did run the installer. I'm actually following an old tutorial exactly just to make sure I'm remembering how to do things right, but it still won't work.   I know I'm supposed to post the log file but no log file generates, the server just pops out that one error message and then crashes.   Any help would be greatly appreciated. I'm lost.
  • Topics

  • Who's Online (See full list)