MFF

    Minecraft Forge France
    • Récent
    • Mots-clés
    • Populaire
    • Utilisateurs
    • Groupes
    • Forge Events
      • Automatique
      • Foncé
      • Clair
    • S'inscrire
    • Se connecter

    Ajouter des NBT TAG a des tile entity

    Planifier Épinglé Verrouillé Déplacé Résolu 1.11.x
    1.11.x
    40 Messages 5 Publieurs 6.7k Vues 1 Watching
    Charger plus de messages
    • Du plus ancien au plus récent
    • Du plus récent au plus ancien
    • Les plus votés
    Répondre
    • Répondre à l'aide d'un nouveau sujet
    Se connecter pour répondre
    Ce sujet a été supprimé. Seuls les utilisateurs avec les droits d'administration peuvent le voir.
    • FlowF Hors-ligne
      Flow
      dernière édition par

      @LeBossMax2:

      Alors, ça c’est que tu n’a pas register ton tileEntity !

      Serait-ce la fatigue ? Je pense, désoler la je suis débile, j’ai rajouté l’enregistrement de la tile entity T_T ^^ Dernière question, comment je suis censé faire dans mon packet ?

      ublic static class Handler implements IMessageHandler <packetvaultcreated, imessage="">{
      
      @Override
      public IMessage onMessage(PacketVaultCreated message, MessageContext ctx)
      {
      EntityPlayer player = ctx.getServerHandler().playerEntity;
      String owner = player.getUniqueID().toString();
      World world = ?;
      BlockPos pos = ?;
      TileEntity te = world.getTileEntity(pos);
      if(te.getTileData().hasNoTags())
      {
      new NBTTagCompound();
      te.getTileData().setString("Owner", owner);
      te.getTileData().setString("password", pass);
      }
      player.closeScreen();
      return null;
      }
      }
      ``` Je ne sais jamais comment instancier mes variables de type world et BlockPos, en fait si je sais à quoi elle correspondent mais ici elle concerne le TileEntity du coup je sais pas quoi mettre :/</packetvaultcreated,>

      Oui ce gif est drôle.

      1 réponse Dernière réponse Répondre Citer 0
      • LeBossMax2L Hors-ligne
        LeBossMax2
        dernière édition par

        Ici, tu veux que l’action soit faite dans le monde où est le joueur donc pour le world, tu peux faire player.world et pour le blockPos, il faut que tu envoie la position du tileEntity dans le packet (sous la forme des 3 coordonnées). Et il ne va pas falloir oublier le null check pour la tileEntity, sinon, il peut y avoir des crash, surtout si quelqu’un s’amuse à envoyer des faux packets au server.

        1 réponse Dernière réponse Répondre Citer 0
        • FlowF Hors-ligne
          Flow
          dernière édition par

          @LeBossMax2:

          Ici, tu veux que l’action soit faite dans le monde où est le joueur donc pour le world, tu peux faire player.world et pour le blockPos, il faut que tu envoie la position du tileEntity dans le packet (sous la forme des 3 coordonnées). Et il ne va pas falloir oublier le null check pour la tileEntity, sinon, il peut y avoir des crash, surtout si quelqu’un s’amuse à envoyer des faux packets au server.

          Pour le blockpos je vois ce que tu veut dire seulement je passe par un Gui pour créer mon chest et dans le gui je n’ai pas la position du block non plus 😕

          Oui ce gif est drôle.

          1 réponse Dernière réponse Répondre Citer 0
          • DeletedD Hors-ligne
            Deleted
            dernière édition par

            Il faut donc que ton gui possède un constructeur avec un paramètre de type BlockPos.

            1 réponse Dernière réponse Répondre Citer 0
            • FlowF Hors-ligne
              Flow
              dernière édition par

              Hummm, genre je dois faire comme ça ?

              protected void actionPerformed(GuiButton button, BlockPos pos) throws IOException
              {
              if(button == this.create)
              {
              if(textbox != null)
              {
              int x = pos.getX();
              int y = pos.getY();
              int z = pos.getZ();
              pass = textbox.toString();
              ModEconomy.network.sendToServer(new PacketVaultCreated(pass, x,y,z));
              }
              }
              }
              
              

              Oui ce gif est drôle.

              1 réponse Dernière réponse Répondre Citer 0
              • DeletedD Hors-ligne
                Deleted
                dernière édition par

                Oui, ou directement envoyer le pos dans ton packet.

                1 réponse Dernière réponse Répondre Citer 0
                • FlowF Hors-ligne
                  Flow
                  dernière édition par

                  @‘Plaigon’:

                  Oui, ou directement envoyer le pos dans ton packet.

                  Ah mince, je pensais avoir fait une erreur car en faisant un débug il n’y a aucune pos…

                  Je dois bien faire comme ça ? BlockPos pos = new BlockPos(x,y,z);

                  Oui ce gif est drôle.

                  1 réponse Dernière réponse Répondre Citer 0
                  • DylemD Hors-ligne
                    Dylem
                    dernière édition par

                    @‘Legrandfifou’:

                    @‘Plaigon’:

                    Oui, ou directement envoyer le pos dans ton packet.

                    Ah mince, je pensais avoir fait une erreur car en faisant un débug il n’y a aucune pos…

                    Je dois bien faire comme ça ? BlockPos pos = new BlockPos(x,y,z);

                    Faudrait que tu m’expliques l’intérêt, vu que tu as déjà le BlockPos à l’entrée de ta fonction ? x)

                    EDIT: Si tu veux ouvrir un screen, prends juste la position du joueur dans onMessage

                    
                    player.getPosition()
                    
                    

                    Si je t'ai aidé, n'hésite pas à cliquer sur le nombre vert en dessous de mon image de profil, pour me le faire savoir. Ca me motive pour continuer …

                    1 réponse Dernière réponse Répondre Citer 0
                    • FlowF Hors-ligne
                      Flow
                      dernière édition par

                      @Dylem:

                      @‘Legrandfifou’:

                      @‘Plaigon’:

                      Oui, ou directement envoyer le pos dans ton packet.

                      Ah mince, je pensais avoir fait une erreur car en faisant un débug il n’y a aucune pos…

                      Je dois bien faire comme ça ? BlockPos pos = new BlockPos(x,y,z);

                      Faudrait que tu m’expliques l’intérêt, vu que tu as déjà le BlockPos à l’entrée de ta fonction ? x)

                      EDIT: Si tu veux ouvrir un screen, prends juste la position du joueur dans onMessage

                      
                      player.getPosition()
                      
                      

                      A récupérer les tags du tileentity posé à ses cordonnées ^^ En fait dans mon packet je récupère un tileentity du coup j’ai besoin de world récupérer au final facilement mais des positions du tileentity donc d’ou le pos , que je passe par le gui ^^

                      Oui ce gif est drôle.

                      1 réponse Dernière réponse Répondre Citer 0
                      • robin4002R Hors-ligne
                        robin4002 Moddeurs confirmés Rédacteurs Administrateurs
                        dernière édition par

                        Solution 1 : envoyer le x, y, z dans le paquet : https://github.com/kevin68/The-Spotlight-Mod/blob/master/src/fr/mcnanotech/kevin_68/thespotlightmod/packets/PacketUpdateData.java#L66
                        Solution 2 : passer par la variable openContainer du joueur pour avoir le container puis le tile entity.

                        1 réponse Dernière réponse Répondre Citer 0
                        • FlowF Hors-ligne
                          Flow
                          dernière édition par

                          C’est la première solution que j’ai faite, mais si je fais comme m’a dit plaigon lors du débug je n’ai pas de position, du coup je l’ai ai remplacé par 0 pour tester et en fait mon te est null je sais pas pourquoi :

                          Mon Gui :

                          
                          protected void actionPerformed(GuiButton button, BlockPos pos) throws IOException
                          {
                          if(button == this.create)
                          {
                          int x = pos.getX();
                          int y = pos.getY();
                          int z = pos.getZ();
                          
                          if(textbox != null)
                          {
                          pass = textbox.toString();
                          ModEconomy.network.sendToServer(new PacketVaultCreated(pass, x,y,z));
                          }
                          }
                          }
                          
                          

                          Mon packet :

                          package fr.fifou.economy.packets;
                          
                          import fr.fifou.economy.blocks.tileentity.TileEntityBlockVault;
                          import io.netty.buffer.ByteBuf;
                          import net.minecraft.entity.player.EntityPlayer;
                          import net.minecraft.nbt.NBTTagCompound;
                          import net.minecraft.tileentity.TileEntity;
                          import net.minecraft.util.math.BlockPos;
                          import net.minecraft.world.World;
                          import net.minecraftforge.fml.common.network.ByteBufUtils;
                          import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
                          import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler;
                          import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;
                          
                          public class PacketVaultCreated  implements IMessage {
                          
                          private static String pass;
                          private static int x;
                          private static int y;
                          private static int z;
                          public PacketVaultCreated()
                          {
                          
                          }
                          
                          public PacketVaultCreated(String pass, int x, int y, int z)
                          {
                          this.pass = pass;
                          this.x = x;
                          this.y = y;
                          this.z = z;
                          }
                          
                          @Override
                          public void fromBytes(ByteBuf buf)
                          {
                          this.pass = ByteBufUtils.readUTF8String(buf);
                          this.x = buf.readInt();
                          this.y = buf.readInt();
                          this.z = buf.readInt();
                          }
                          
                          @Override
                          public void toBytes(ByteBuf buf)
                          {
                          ByteBufUtils.writeUTF8String(buf, this.pass);
                          buf.writeInt(this.x);
                          buf.writeInt(this.y);
                          buf.writeInt(this.z);
                          }
                          
                          public static class Handler implements IMessageHandler <packetvaultcreated, imessage="">{
                          
                          @Override
                          public IMessage onMessage(PacketVaultCreated message, MessageContext ctx)
                          {
                          EntityPlayer player = ctx.getServerHandler().playerEntity;
                          String owner = player.getUniqueID().toString();
                          World world = player.world;
                          TileEntityBlockVault te = (TileEntityBlockVault) world.getTileEntity(new BlockPos(x,y,z));
                          System.out.println(te);
                          System.out.println(x);
                          System.out.println(y);
                          System.out.println(z);
                          if(te != null)
                          {
                          if(te.getTileData().hasNoTags())
                          {
                          
                          te.getTileData().setString("Owner", owner);
                          te.getTileData().setString("password", pass);
                          player.closeScreen();
                          }
                          }
                          
                          return null;
                          }
                          }
                          }
                          

                          EDIT : Je pense que c’est le BlockPos qui ‘merde’, si je remplace par 0 mon packet est envoyer, mais si je remplace par x,y,z la rien ne s’envoie, pas de débug, rien …</packetvaultcreated,>

                          Oui ce gif est drôle.

                          1 réponse Dernière réponse Répondre Citer 0
                          • DeletedD Hors-ligne
                            Deleted
                            dernière édition par

                            Attention à penser également à rajouter un instanceof car n’importe quel client pourrait t’envoyer des packets avec des coords de te qui pourraient très bien être des TileEntityChest, TileEntityFurnace, etc…Et là c’est le crash assuré.
                            Montre moi le constructeur de ton gui, ton GuiHandler, ainsi que la fonction onBlockActivated, stp.

                            1 réponse Dernière réponse Répondre Citer 0
                            • FlowF Hors-ligne
                              Flow
                              dernière édition par

                              GuiVaultNew

                              package fr.fifou.economy.gui;
                              
                              import java.io.IOException;
                              
                              import org.lwjgl.input.Keyboard;
                              
                              import fr.fifou.economy.ModEconomy;
                              import fr.fifou.economy.blocks.tileentity.TileEntityBlockVault;
                              import fr.fifou.economy.packets.PacketVaultCreated;
                              import net.minecraft.client.Minecraft;
                              import net.minecraft.client.gui.GuiButton;
                              import net.minecraft.client.gui.GuiScreen;
                              import net.minecraft.client.gui.GuiTextField;
                              import net.minecraft.entity.player.EntityPlayer;
                              import net.minecraft.item.ItemStack;
                              import net.minecraft.tileentity.TileEntity;
                              import net.minecraft.util.ResourceLocation;
                              import net.minecraft.util.math.BlockPos;
                              import net.minecraft.util.text.TextFormatting;
                              import net.minecraft.world.World;
                              import net.minecraftforge.fml.client.config.GuiButtonExt;
                              
                              public class GuiVaultNew extends GuiScreen
                              {
                              private static final ResourceLocation background = new ResourceLocation(ModEconomy.MODID ,"textures/gui/screen/gui_item.png");
                              
                              protected int xSize = 256;
                              protected int ySize = 124;
                              protected int guiLeft;
                              protected int guiTop;
                              
                              private String owner = "";
                              private String pass = "";
                              
                              private GuiTextField textbox;
                              private GuiButtonExt create;
                              
                              @Override
                              public void initGui()
                              {
                              this.guiLeft = (this.width - this.xSize) / 2;
                                 this.guiTop = (this.height - this.ySize) / 2;
                                 textbox = new GuiTextField(1, fontRendererObj, width / 2 - 63, height / 2 - 13 , 126, 26);
                              this.buttonList.add(this.create = new GuiButtonExt(1, width / 2 + 75, height / 2 + 25, 35, 20,  "Create"));
                              }
                              
                              @Override
                              protected void keyTyped(char typedChar, int keyCode) throws IOException
                              {
                              textbox.textboxKeyTyped(typedChar, keyCode);
                              super.keyTyped(typedChar, keyCode);
                              }
                              
                              @Override
                              protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException
                              {
                              textbox.mouseClicked(mouseX, mouseY, mouseButton);
                              super.mouseClicked(mouseX, mouseY, mouseButton);
                              }
                              @Override
                               public boolean doesGuiPauseGame()
                                 {
                                     return false;
                                 }
                              
                              protected void actionPerformed(GuiButton button, BlockPos pos) throws IOException
                              {
                              if(button == this.create)
                              {
                              int x = pos.getX();
                              int y = pos.getY();
                              int z = pos.getZ();
                              
                              if(textbox != null)
                              {
                              pass = textbox.toString();
                              ModEconomy.network.sendToServer(new PacketVaultCreated(pass, x,y,z));
                              }
                              }
                              }
                              
                              @Override
                              public void updateScreen()
                              {
                              textbox.updateCursorCounter();
                              super.updateScreen();
                              }
                              
                              @Override
                              public void drawScreen(int mouseX, int mouseY, float partialTicks)
                              {
                              
                              this.drawDefaultBackground();
                                     // added
                                     this.mc.getTextureManager().bindTexture(background);
                                     int i = this.guiLeft;
                                     int j = this.guiTop;
                                     this.drawTexturedModalRect(i, j, 0, 0, this.xSize, this.ySize);
                                     textbox.drawTextBox();
                              super.drawScreen(mouseX, mouseY, partialTicks);
                              }
                              }
                              
                              

                              onBlockActivated

                              
                              @Override
                              public boolean onBlockActivated(World worldIn, BlockPos pos,IBlockState state, EntityPlayer playerIn, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ) 
                              {
                                  EntityPlayer player = Minecraft.getMinecraft().player;
                                  TileEntity te = worldIn.getTileEntity(pos);
                              
                                  if(te.getTileData().hasKey("Owner"))
                                  {
                                  this.setBlockUnbreakable();
                                  this.setResistance(20000.0F);
                                  }
                                  else
                                  {
                                  System.out.println(te.getTileData().hasKey("Owner"));
                                  if (worldIn.isRemote)
                                          {
                                  playerIn.openGui(ModEconomy.instance, GuiHandler.BLOCK_VAULT_NEW, worldIn, 0, 0, 0);
                                          }
                                  }
                              
                                       return true;
                                   }
                              
                              

                              GuiHandler

                              package fr.fifou.economy.gui;
                              
                              import javax.annotation.Nullable;
                              
                              import org.lwjgl.opengl.GL11;
                              
                              import fr.fifou.economy.ModEconomy;
                              
                              import net.minecraft.client.gui.inventory.GuiContainer;
                              import net.minecraft.client.resources.I18n;
                              import net.minecraft.entity.player.EntityPlayer;
                              import net.minecraft.entity.player.InventoryPlayer;
                              import net.minecraft.inventory.IInventory;
                              import net.minecraft.tileentity.TileEntity;
                              import net.minecraft.util.ResourceLocation;
                              import net.minecraft.util.math.BlockPos;
                              import net.minecraft.world.World;
                              import net.minecraftforge.fml.common.network.IGuiHandler;
                              import net.minecraftforge.fml.relauncher.Side;
                              import net.minecraftforge.fml.relauncher.SideOnly;
                              
                              public class GuiHandler implements IGuiHandler
                              {
                              public static final int ITEM_CARD_GUI = 0;
                              public static final int ITEM_CARD_DELETE = 1;
                              public static final int BLOCK_VAULT_NEW = 2;
                              
                              public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z)
                              {
                              return null;
                              }
                              
                              public Object getClientGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z)
                              {
                              if(ID == ITEM_CARD_GUI)
                              {
                              return new GuiItem();
                              }
                              else if(ID == ITEM_CARD_DELETE)
                              {
                              return new GuiDelete();
                              }
                              else if(ID == BLOCK_VAULT_NEW)
                              {
                              return new GuiVaultNew();
                              }
                              return null;
                              }
                              
                              }
                              
                              

                              Pour l’instanceof tu veut dire dans le packet ?

                              Oui ce gif est drôle.

                              1 réponse Dernière réponse Répondre Citer 0
                              • DeletedD Hors-ligne
                                Deleted
                                dernière édition par

                                Le problème se situe au niveau des paramètres d’opengui. Renseigne toi pour les compléter intelligemment.
                                La seconde solution serait tout de même bien plus facile !

                                1 réponse Dernière réponse Répondre Citer 0
                                • LeBossMax2L Hors-ligne
                                  LeBossMax2
                                  dernière édition par

                                  Tu ne peux pas changer les paramètres de la fonction actionPerformed pour ajouter le blockPos :
                                  @‘Plaigon’:

                                  Il faut donc que ton gui possède un constructeur avec un paramètre de type BlockPos.

                                  Et tu enregistre le blockPos dans une variable de ton GuiVaultNew.
                                  (Ou alors, tu utilise la deuxième solution)

                                  1 réponse Dernière réponse Répondre Citer 0
                                  • FlowF Hors-ligne
                                    Flow
                                    dernière édition par

                                    @Plaigon:

                                    Le problème se situe au niveau des paramètres d’opengui. Renseigne toi pour les compléter intelligemment.
                                    La seconde solution serait tout de même bien plus facile !

                                    Hmm, je ne vois pas le problème avec le openGui ^^

                                    @LeBossMax2:

                                    Tu ne peux pas changer les paramètres de la fonction actionPerformed pour ajouter le blockPos :
                                    @‘Plaigon’:

                                    Il faut donc que ton gui possède un constructeur avec un paramètre de type BlockPos.

                                    Et tu enregistre le blockPos dans une variable de ton GuiVaultNew.
                                    (Ou alors, tu utilise la deuxième solution)

                                    La deuxième solution je vois pas du tout, je me doute que c’est player.openContainer, mais comment je récupère le tileentity Oo ?

                                    Oui ce gif est drôle.

                                    1 réponse Dernière réponse Répondre Citer 0
                                    • LeBossMax2L Hors-ligne
                                      LeBossMax2
                                      dernière édition par

                                      Après réflexion, je ne vois pas l’utilisée de la deuxième solution, puisque tu n’as pas de container. Donc il faudrait en créer un et changer ce que tu dois aussi changer pour la première solution (le openGui). Donc au final, la solution 1 est plus simple.

                                      1 réponse Dernière réponse Répondre Citer 0
                                      • DeletedD Hors-ligne
                                        Deleted
                                        dernière édition par

                                        Tu ne vois pas le problème dans tes paramètres ? Un moment donné, il va quand même falloir faire un minimum de recherches. La javadoc est là pour t’aider, mais j’imagine que tu ne l’as même pas lu… Moi je vois chez moi ceci

                                        
                                           /**
                                            * Opens a GUI with this player, uses FML's IGuiHandler system.
                                            * Allows for extension by modders.
                                            *
                                            * @param mod The mod trying to open a GUI
                                            * @param modGuiId GUI ID
                                            * @param world Current World
                                            * @param x Passed directly to IGuiHandler, data meaningless Typically world X position
                                            * @param y Passed directly to IGuiHandler, data meaningless Typically world Y position
                                            * @param z Passed directly to IGuiHandler, data meaningless Typically world Z position
                                            */
                                           public void openGui(Object mod, int modGuiId, World world, int x, int y, int z)
                                           {
                                               net.minecraftforge.fml.common.network.internal.FMLNetworkHandler.openGui(this, mod, modGuiId, world, x, y, z);
                                           }
                                        
                                        

                                        Alors ça m’étonnerait qu’il faille mettre 3 zéros à la fin !

                                        Ensuite concernant la solution B, tu peux très bien get une TileEntity à partir d’une instance de ta classe Container, bien évidemment, si ce dernier possède un constructeur avec et une variable, etc…

                                        EDIT = je suis également allé trop vite à la lecture de tes classes. Ton GuiHandler ne possède même pas de référence à un quelconque container, alors que tu t’apprêtes à faire un coffre. Donc là aussi, il y a un point à revoir.

                                        1 réponse Dernière réponse Répondre Citer 0
                                        • FlowF Hors-ligne
                                          Flow
                                          dernière édition par

                                          @‘LeBossMax2’:

                                          Après réflexion, je ne vois pas l’utilisée de la deuxième solution, puisque tu n’as pas de container. Donc il faudrait en créer un et changer ce que tu dois aussi changer pour la première solution (le openGui). Donc au final, la solution 1 est plus simple.

                                          C’est ce que je me suis dis aussi, ici ce n’est qu’un guiScreen, j’aurais un container par la suite mais la je suis un peu perdu xDD Pour ce qui est du openGui, vous parlez de 0,0,0 ? Car j’ai repris ce que robin m’avait dis dans un sujet que il fallait mettre 0,0,0 ^^

                                          Du coup si je comprend bien je dois passer les variables x,y,z dans mon packet mais pour les récupérer dans mon gui je dois créer un constructeur qui a comme spécificité d’avoir le blockPos ?

                                          EDIT : Je récapitule mon problème pour être au clair, et au calme espérons le ^^ Mon but est de faire un coffre sécurisé, donc en posant le coffre la tileentity n’a aucun tag et propose alors de remplir un ‘formuaire’ avec un choix de password une fois que il a appuyé sur create, la tilenentity a des tags et du coup proposera un nouveau GuiScreen qui vérifiera le mot de passe, si il est correct j’ouvre un container, si il est incorrecte il dois réessayer, ici je suis a la première étape, c’est à dire la création du mot de passe, et l’application au tile entity des données. C’est la première fois que j’essaye d’appliquer des datas à un TileEntity donc j’ai peut-être pas tout pigé de suite et je m’en excuse mais pour ce qui est du gui je vais quote ce que Robin m’avais dit :

                                          @‘robin4002’:

                                          Oui.
                                          player.openGui(ClassePrincipale.instance, GuiHandler.ITEM_CARD_GUI, 0, 0, 0); (pas besoin de s’embêter avec x, y, z).

                                          Cependant je ne sais pas si ici il faut s’embêter avec des coordonnées mais je ne pense pas …

                                          Merci encore de m’aider ^^

                                          Oui ce gif est drôle.

                                          1 réponse Dernière réponse Répondre Citer 0
                                          • DeletedD Hors-ligne
                                            Deleted
                                            dernière édition par

                                            A la limite ne t’embête pas, avec les coordonnées, je vérifierai si elles sont vraiment importantes.
                                            Ton premier gui d’enregistrement du mdp, doit donc avoir un référence à ta te grâce à la méthode World#getTileEntity, donc ça sous-entend bien d’avoir un blockpos dans le packet, donc précédemment dans le gui, donc précédemment dans le guihandler. Une méthode pour transférer le pos de la te jusqu’au gui, serait d’enregistrer les 3 coordonnées dans les tags du joueur, puis ensuite dans ta méthode GuiHandler#getClientGuiElement, de les ressortir pour les joindre au constructeur de ton gui, puis finalement de les retirer des tags des joueurs pour les prochaines interactions de coffres sécurisés.

                                            1 réponse Dernière réponse Répondre Citer 0
                                            • 1
                                            • 2
                                            • 1 / 2
                                            • Premier message
                                              Dernier message
                                            Design by Woryk
                                            ContactMentions Légales

                                            MINECRAFT FORGE FRANCE © 2024

                                            Powered by NodeBB