Solved Extending enum

Discussion in 'Spigot Plugin Development' started by Synapz, Jun 9, 2016.

  1. I made a MinigameAPI-- more of a core. Which lets me create miniagmes with only one line. Then all I have to do is add gameplay features.

    MinigameAPI has things like leaderboards, signs, basic commands (join leave set spawn, etc), holograms, messages, scoreboards, everything really.

    So I encountered an issue. And it is a complex issue from the way I have my messages file setup. I have it made so messages are all configurable and easily made (all you need to do is make a new enum with the default string) here is how it works:

    • MinigameAPI is loaded
    • Minigame loads
    • New Minigame API is created
    • MinigameAPI loads all of its messages
      • There is a Messages.java enum class with things like JOIN_ARENA("you joined"
      • It turns the enum name to a path by converting _ to - and making it lowercase
      • If the value is not found in messages.yml it uses the created path to set the default string to what's inside the parameter
      • Otherwise it loads the string from the path
    However in my Minigame.... I cannot "extend" the Messages enum class-- which makes sense.

    Anyone have any ideas for this?
    Like if I want to make a new enum named SKYWARS_FALL("you fell and died"); then have it automatically be picked up by the plugin and loaded aswell.

    I have been thinking about this for awhile but I cannot find any solid solutions

    tl;dr
    How can I add more messages to an enum from Minigame API in a separate Minigame plugin.
     
    #1 Synapz, Jun 9, 2016
    Last edited: Jun 9, 2016
  2. Impossible to add enums, that is against the point of them. Just make another ENUM. like Messages.SKYWARS_FALL
     
  3. I know :p,
    The title is just for showing you basically what I am trying to do through other ways.

    I know but then this is not really an API... Lets say I use the API for SkyWars and Paintball, I do not want Paintball messages to load on SkyWars and SkyWars messages to load on Paintball. Of course, I can add a parameter for the type of plugin it is... So inside MinigameAPI messages it would be ARENA_JOIN("Successfully Joined", Minigame.ALL) and then SW_FALL("You have fallen!", Minigame.SKYWARS). And this would be set in the new MinigameAPI object parameters. But, then it is not really an API because I have to edit the API itself before I can even use the plugin. API are suppose to be built upon, not worked inside it and then inside your minigame. You just want to extend the API and have the API do all the work.


    So maybe a Messages object with a message inside it. Same as enums except I would load all the Messages by creating a bunch of message objects, then I can extend Messages and do like, super.add(new Message("")) and it would add that to messages.yml... But then I can't do like Messages.ARENA_JOIN, I would have to do like Messages.getMessages().get(index of message) when trying to get it out of the arraylist it would be stored in. So that won't work...

    See I keep going back and forth on things... I cant figure this out
     
  4. Make a wrapper for the enum?

    Imo this is really not a good use of enumerators, you should be using const strings or something.
     
    • Agree Agree x 1
  5. No. I also was suggested to use enums by multiple people. And this:

    Code (Text):
    ARENA_JOIN("Joined");
    ARENA_INFO("Info");
    ... 500 other messages ...

    private static void loadMessages() {
        for (Message message : Messages.values())) {
            String path = message.toString();
            String value = config.getString(path);
       
            if (value == null) {
                value = message.getDefaultString();
                config.set(path, value);
            }

            message.setString(value);
        }
        save();
    }
     
    If way way way way better than something like this...
    Code (Text):
    private static final ARENA_JOIN;
    private static final ARENA_INFO;
    ... 500 other constants ...

    public void loadMessages() {
            ARENA_JOIN = config.getString("arena-join");
            ARENA_INFO = config.getString("arena-info);
            ... 500 other values to set ...
            // I would probably anyway have to make another method
            // like getString(path); which sees if it is null and if it is null I need another parameter with the default and set it
    }
    Yeah. I'm going to keep enums it is definitely the better way of doing this :p. For the enum I just need to make a new enum and put a string in it and the load method does the rest. I would take 502 lines than 1004 anyway don't know about you... The whole enum idea was from @DarkSeraphim (and from another guy) and knows what he is doing ;). I had constants at first though.a and trust me this is the better way. But then again I save so much time only to get stuck with this issue. Constants would not have this issues

    What do you mean exactly?
     
    #5 Synapz, Jun 9, 2016
    Last edited: Jun 9, 2016
  6. Multiple people suck at programming.


    A wrapper class for the enums, so you could put it in a list or otherwise make it dynamic?


    Oh, i see, you want to load them dynamically. oops. Your best bet is either a wrapper class, or a map.
    The advantage of a map is that it is much more extensible, you do not need to hardcode every value it would expect.

    There is no possible way to do this with enums that would be more efficient than simply using a hashmap of <NAME,message>, which could be loaded from an array of strings containing each config path.

    The closest thing you could do with enums would be an abstract class that has a method that searches the contained enumerator for a string using the valueOf and does a null check, which is complex and nowhere near as fast as using a hashmap.

    Or to make it even more dynamic and simple, you could simply put it all into the config file and use multiple instances of a single class.
     
    #6 FlyingLlama, Jun 9, 2016
    Last edited: Jun 9, 2016
  7. @Synapz extending enums ís possible through some next-level reflection, but there are way better ways of doing this. This is something I recently used:
    1. Create an interface, something along the lines of Message
    2. Give the interface all the methods you you need.
    3. Create the enun with some default messages and let the enum implement the interface.
    4. Create a class which implements the interface as well, but this time make it possible for users to create it themselves.
    5. Done!
    What I had done extra though is make the custom class have a private constructor and a method which creates a new one. It first checks, through a map containing all current values by their name, if already such a thing exists, and if it does it returns it. (Does the same for each default value). If it doesn't exist, create one, put it in the map and return it.
     
    • Like Like x 1
    • Agree Agree x 1
  8. Hah I agree but 1) I would never listen to someone who does not have any expirence and 2) I saw myself enums were way better loading, saving, and having things auto generate with just 1 extra line.

    Not sure what you mean by every value, if you mean the enum parameters I have in the enum (ARENA_JOIN("Joined" <-- that)? The enum parameters I need because if the message is not there, it will set it inside messages.yml so I need a default anyway. But what you are saying is like this:
    Code (Text):

    // first string is like "arena-name" second trying is what is held
    private static final Map<String, String> messages = new HashMap<>() {{
        put("arena-join", "Successfully joined!");
    }};

    public void loadMessages() {
           for (String path : messages.keyset()) {
                // if not found in config set it from the default
               // else set the value of it from whatever is inside
                if (config.getString(path) == null) {
                   config.set(path, messages.get(path);
               }
               messages.replace(path, messages.get(path), config.getString(path));
           }
    }
    Then to get a message I would do messages.get("arena-name") and it would return what I need. This looks very nice although I make many mistakes on accident so I see myself typing in messages.get("aaaarena-join") and or totally miss the spelling. But I guess I can even create constants for the names...

    I like this way a lot. Will try to see if I can come up with something using this. If this does not work either I will try @FlyingLlama's method ;) thank you guys!!
     
  9. Woah, r00d! :eek:

    Anyways, I'd be doing something quite similar to what @megamichiel has suggested, perhaps mixing in some placeholders for generic messages (i.e. a START_GAME message in a DefaultMessage enum).

    The interface would also allow you to easily decorate the messages, f.e. adding a prefix, or changing a colour, by simply providing a class which takes the original String and prepends a colour or prefix.

    But yea, as a base for message constants, I'd still consider enums as the way to go - plugins generally require you to programmatically add new messages regardless (excluding List<String> handling in the config, of course), in which case the interface provides you with that flexibility as well.
     
    • Like Like x 1
  10. Thanks for clarifying things :) I am going to try his method and if I have any issues I will post here when I have time.

    Thanks a lot for the help everyone!
     
  11. This is definitely going to work. I added some implementation of it and so far it works great. Thanks so much for the help.
     
  12. If you made it 'public class Messages extends Enum', you did it wrong.. (That's what it sounded like anyways). It should be 'public enum Messages'. Also you can do 'public enum Messages()' - as a method. Don't forget if you do an enum file, you need to define the enum. Just a little tip with enums ;).
     
  13. No that's not my question. I know how enums work, it was a little more complicated than that. The title was a little misleading, sorry. But that wasn't my issue :p. Thanks though
     
  14. No problem XD.
     
  15. @Synapz I'm glad it's working. It might be smart to mark the thread as solved now ;p
     
    • Like Like x 1