-2

I want to create a list, add blocks to it and then use it in a BlockBreakEvent to check if the block is in the list and cancel the event if it's not. But I can't seem to create and add things in it any other way than in the actual event itself (which looks to me like it would create issues). The only thing that is working for me is creating the list in the event and adding blocks to it one by one which looks really messy compared to: creating the list in a separate class and just checking the list with if(Listname.contains(block)) does anyone know how I can achieve this? Whether its dependency injection, or whatever else. I just can't figure out how to put it to use.

Here's what I've tried and is currently working for me, but I believe it's theoretically incorrect:

public class Event implements Listener {
@EventHandler
public void onBreak(BlockBreakEvent e) {
    List<Material> allowedblocks = new ArrayList<Material>();
    allowedblocks.add(Material.STONE);
    //repeat this 10-50 times for whatever item
    
    
    Player p = e.getPlayer();
    Material block = e.getBlock().getType();
    
    if(allowedblocks.contains(block)){
        p.sendMessage("Invalid block. Break cancelled");
        e.setCancelled(true);
    }else{
        p.sendMessage("Valid Block");
    }
    }

}
Dawood ibn Kareem
  • 77,785
  • 15
  • 98
  • 110
  • Save the list somewhere that is not a local variable and use it from your other location? Start with a global variable. If you don't show what you have done, this question is too open ended. There are many options and no single answer would be more correct than the other. – Ruan Mendes Jul 05 '22 at 20:01
  • @JuanMendes Could you give me an example? –  Jul 05 '22 at 20:04
  • Create a global singleton and store the data there. – Ruan Mendes Jul 05 '22 at 20:10
  • I'm not sure how to do that –  Jul 05 '22 at 20:11
  • 1
    Then you should ask a question about that. I know you want someone to just help you solve your problem but Stack Overflow is for questions that can be helpful to others also. See https://softwareengineering.stackexchange.com/questions/344365/creating-a-singleton-to-access-static-data – Ruan Mendes Jul 05 '22 at 20:27

4 Answers4

1

You can make allowedBlocks List a class field and fill it with elements inside of the constructor.

public class YourClass {
    private List<Material> allowedBlocks = new ArrayList<>();    

    public YourClass() {
        allowedBlocks.add(Material.STONE);
        //repeat this 10-50 times for whatever item
    }

    @EventHandler
    public void onBreak(BlockBreakEvent e) {
        Player p = e.getPlayer();
        Material block = e.getBlock().getType();
    
        if(allowedBlocks.contains(block)){
            p.sendMessage("Valid Block");
        } else {
            p.sendMessage("Invalid block. Break cancelled");
            e.setCancelled(true);
        }
    }
}

Another approach would be to make the list static and fill it with values inside of a static block. I would not recommend making the list static if you are planning to change its values, but if your allowed blocks are going to remain the same, it may be a good idea to even go further and make it public, so you can access it from anywhere without an instance of YourClass

public class YourClass {
  public static final List<Material> allowedBlocks;    

  static {
      List<Materials> list = new ArrayList<>();
      list.add(Material.STONE);
      //repeat this 10-50 times for whatever item

      //use an unmodifiable list, 
      //so you do not accidentally change its content later
      allowedBlocks = Collections.unmodifiableList(list);
  }

  @EventHandler
  public void onBreak(BlockBreakEvent e) {
      Player p = e.getPlayer();
      Material block = e.getBlock().getType();

      if(allowedBlocks.contains(block)){
          p.sendMessage("Valid Block");
      } else {
          p.sendMessage("Invalid block. Break cancelled");
          e.setCancelled(true);
      }
  }
}

In the first case, there will be a list of allowedBlocks per instance of YourClass, which means, that every time you call new YourClass() a new List will be created and filled. In the second case, there will be only one list which will be created and populated on class loading (at the very beginning of the program) start up.

P.S. I would rather use a Set instead of a List here, considering you are using contains very often.

Alexandr Ter
  • 151
  • 5
0

Since you are using an enum to store your Material types, you can simply call the static .values() method through Material.

Ex:

@EventHandler
public void onBreak(BlockBreakEvent e) {
    Player p = e.getPlayer();
    Material block = e.getBlock().getType();
    if(List.of(Material.values()).contains(block)){
        p.sendMessage("Invalid block. Break cancelled");
        e.setCancelled(true);
    }else{
        p.sendMessage("Valid Block");
    }
    }

}

If you need to be able to customize what values are in the List you can use the singleton pattern to access that information globally.

The instance can be accessed statically from anywhere in the application:

import java.util.List;

public class BlockController {
        
    public static BlockController instance = new BlockController();
    
    private List<Material> allowedBlocks; 
    
    public BlockController() {
        this.allowedBlocks = new ArrayList<>();
    }
    
    public void addAllowedBlock(Material mat) {
        this.allowedBlocks.add(mat);
    }
    
    public void removeAllowedBlock(Material mat) {
        this.allowedBlocks.remove(mat);
    }
    
    public boolean containsBlock(Material mat) {
        return this.allowedBlocks.contains(mat);
    }
    
    public void clear() {
        this.allowedBlocks.clear();
    }
    
/**
 * You can add more functionality here...
 * This class can be accessed anywhere in the application
 * 
 * use:
 * 
 * BlockController controller = BlockController.instance;
 * controller.containsBlock(Material.BLOCK);
 */

}

  • Consider when the block Material would ever not be included in `Material.values()`. – Andy Thomas Jul 05 '22 at 20:16
  • I'm not sure where the list is in this example –  Jul 05 '22 at 20:18
  • The list would be `List.of(Material.values())` and if you need to filter it, you could do so. – Luke Harwood Jul 05 '22 at 20:21
  • if you would avoid it what should I use then? I don't understand –  Jul 05 '22 at 20:21
  • @LukeHarwood I need to add things to the list –  Jul 05 '22 at 20:22
  • Are you always adding all types of 'Material's to the allowedblocks? – Luke Harwood Jul 05 '22 at 20:23
  • @LukeHarwood - if the OP were, then he or she would not need to check for allowed materials. – Andy Thomas Jul 05 '22 at 20:24
  • @123909182441598 It wouldn't be terrible to just use a Singleton to store the allowedBlocks if that list changes over time when the event occurs. Singletons should just be used with caution because of over accessibility to elements. – Luke Harwood Jul 05 '22 at 20:30
  • @LukeHarwood what do you mean? I'll try to explain my goal. One class where I create the list and use allowedblocks.add(block) for each block I want allowed. Then I want to be able to go to my event class and reference the list I just created with if(List.contains(the block they tried to break) and then either cancel it or let them break it according to if the block is in the list or not –  Jul 05 '22 at 20:32
0

Try to create the List in a static context. This way the list is the same for all instances:

public class MyClass {
    
    public static List<Material> allowedblocks = new ArrayList<Material>();

    @EventHandler
    public void onBreak(BlockBreakEvent e) {
        allowedblocks.add(Material.STONE);
        ...

Then you can call the List from everywhere like this (e.g. if statement):

if(MyClass.allowedblocks.contains(block)) 

Your problem seems similar to this question, maybe this answer helps too: .

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Japhei
  • 565
  • 3
  • 18
  • I don't want to assign the blocks on break, that's what I'm trying to avoid. I wanted to have a list in another class, and check it in BlockBreakEvent –  Jul 05 '22 at 20:14
  • That is how you put the list in another class and can access it from anywhere. Could you be more specific what the problem with this is? – Japhei Jul 05 '22 at 20:17
  • I'm not very good at this so my explanation probably won't be very good. I want to assign the blocks in the same class that I create the list, and then check it whenever the BlockBreakEvent is called. My problem is if I make the list in an outside class I 1 can't use allowedblocks..add() 2 I can't call allowedblocks whatsoever –  Jul 05 '22 at 20:20
  • 1
    I think your problem is that you are focusing on the onBreak method too much. It is not the whole java file. Take a closer look at the surrounding stuff. – Queeg Jul 05 '22 at 20:22
  • @HiranChaudhuri the on break method is the main part. I need to check if the block is in the list when they try to break it. –  Jul 05 '22 at 20:27
  • 1
    Even if it is the main part, there is something around it. Stuff that is in the same class yet not in the onBreak method. If you are not allowed to store the data outside the method, you cannot do what you want to achieve. – Queeg Jul 05 '22 at 20:31
  • @HiranChaudhuri I see what you're saying. The goal was a separate class for organization purposes, but as long as it's not in the actual onBreak itself I guess it's fine. even then this answer still doesn't do that as I want the list contents added separate from the onBreak (for example I'd like to put the .add() above the EventHandler if that makes sense –  Jul 05 '22 at 20:35
  • You can put your list (`public static List allowedblocks = new ArrayList();`) in any class you want if this anseres your question. You can then access the list via the class name a `.` and then the list name `allowedblocks`. – Japhei Jul 05 '22 at 20:39
0

One approach to creating the list in a separate class is to use a static initializer block:

public class MyClass {
    public static final List<Material> ALLOWED_MATERIALS = new ArrayList<>();

    static {
        ALLOWED_MATERIALS.add( Material.STONE );
    }

    public static List<Material> getAllowedMaterials() {
        return Collections.unmodifiableList( ALLOWED_MATERIALS );
    }
    ...
 }
Andy Thomas
  • 84,978
  • 11
  • 107
  • 151
  • I'm unsure how I would use this in an outside class. I just tried and I can't use ALLOWED_MATERIALS or getAllowedMaterials. How would I put a check into my OnBreak method? –  Jul 05 '22 at 20:28
  • @123909182441598 - Sorry, I omitted the `static` from the method declaration. You can access it from another class like this: `MyClass.getAllowedMaterials()` -- after importing MyClass. – Andy Thomas Jul 05 '22 at 20:42