OnDisable() Force

Discussion in 'Plugin Development' started by 97WaterPolo, Nov 26, 2014.

Thread Status:
Not open for further replies.
  1. Offline

    97WaterPolo

    Is there a way to force your plugin to automatically be the last one to be unloaded from the server?
     
  2. Offline

    mythbusterma

  3. Offline

    Tecno_Wizard

    97WaterPolo, i believe there is a property of the plugin manager that may be able to do this.
     
  4. Offline

    Code0

    mythbusterma I'd just like to note that replies like that just don't contribute to threads. Please only post constructive things.
     
    ChipDev and 97WaterPolo like this.
  5. Offline

    Skionz

    Code0 If we know why then we can maybe find another solution.
     
  6. Offline

    mythbusterma

    This, also, you're being even less constructive. Your backseat moderation is almost laughable in its uselessness.

    I ask because there's probably a much easier solution to this issue.
     
  7. Offline

    Tecno_Wizard

    97WaterPolo, after experimenting, I'm pretty sure there is no way. You can set the order in which they enable, but not disable
     
  8. 97WaterPolo You can load before plugins but I am not sure about disabling after.
     
  9. Offline

    xTrollxDudex

    97WaterPolo
    In order to do this, you must understand how all plugins are disabled.

    The collection of plugins in question is at this line: https://github.com/Bukkit/Bukkit/bl...rg/bukkit/plugin/SimplePluginManager.java#L43

    Plugins are disabled by filling an empty array with the contents of the collection synchronously: https://github.com/Bukkit/Bukkit/bl...rg/bukkit/plugin/SimplePluginManager.java#L43

    I haven't investigated why it is synchronized, or why it decidedly supports multithreading yet, but nevertheless, the array returned by that method is iterated by this method: https://github.com/Bukkit/Bukkit/bl...g/bukkit/plugin/SimplePluginManager.java#L413

    But, here is the gotcha: the array is iterated backwards. Because ArrayList#toArray(T[]) returns the array in insertion order, you would need to insert the plugin FIRST in order to have it disabled last.

    This is quite simple, as it is a matter of swapping the position of your plugin and the first plugin after all the plugins have finished loading into the list. You can be sure that it runs after all the plugins load, and before they unload by checking for the server's shutdown message. I have not investigated thoroughly all the conventional ways, but I believe this is the only way.

    To avoid having a Thread that would continuously waste server resources to check the output, we will add a Handler to the server's logger to watch the output.
    PHP:
    @Override public void onEnable() {
        
    this.getServer().getLogger().addHandler(new Handler() {
            @
    Override public void close() {}
            @
    Override public void flush() {}
            @
    Override public void publish(LogRecord record) {
                if (
    record.getMessage().equals("Stopping server") {
                    
    reorderPlugins();
                }
            }
            @
    Override reportError(String msgException exint code) {}
        });
    }
    There! now we only need to implement the reorderPlugins() method.
    PHP:
    class FieldMod {
        private final Field field;
        public FieldMod(Field field) { this.field = field; }
        public static FieldMod of(Class<?cString field) {
            try {
                
    Field field c.getDeclaredField(field);
                
    field.setAccessible(true);
                return new 
    FieldMod(field);
            } catch (
    Exception x) {
                
    x.printStackTrace();
                return 
    null;
            }
        }
        public 
    Object get(Object othrows Exception {
            return 
    this.field.get(o);
        }
     
        public 
    void set(Object oObject valuethrows Exception {
            
    this.field.set(ovalue);
        }
     
        public 
    Field get() {
            return 
    this.field;
        }
    }
     
    private static final 
    FieldMod SETTER FieldMod.of(Unsafe.class, "theUnsafe");
    private static final 
    FieldMod LIST = FieldMod.of(SimplePluginManager.class, "plugins");
     
    // This should be in the plugin main class
    public void reorderPlugins() {
        try {
            
    PluginManager instance Bukkit.getServer().getPluginManager();
            
    Unsafe unsafe SETTER.get(null);
            List<
    Plugin> list = LIST.get(instance);
     
            
    Plugin old = list.get(0);
            if (
    old != this) { // Just to make sure this is not the first plugin already
                
    int index = list.indexOf(this);
                list.
    set(0this);
                list.
    set(indexold);
            } else return;
     
            
    long off unsafe.objectFieldOffset(LIST.get());
            
    unsafe.putObject(instanceoff, list);
        } catch (
    Exception x) {
            
    x.printStackTrace();
        }
    }
    Try it out and see if it works.

    The reason we get sun.misc.Unsafe because it is a large hassle to set the field without it. You need to rewrite the modifiers for the Field object in order to not get an exception thrown when reflecting a final field.

    Basically what we are doing is setting the plugin to the first element, and setting the plugin that was originally there to our plugin's original spot. When the plugins are disabled, it starts from the last element in the list, and then iterates backwards, and the last one should be the first element, which we set to our plugin. It's pretty hacky but it should work, good luck.

    Edit: Oops, Handler might be called after the string has been formatted, you should use contains(...) a the risk of false positives in plugins that use the same String, or use regex to check for the time/date/and other data prepending the string.
     
    Code0, 97WaterPolo, Totom3 and 4 others like this.
  10. Offline

    97WaterPolo

    xTrollxDudex
    Geesh, I was hoping there was just a simple fix. Thanks for explaining it! Will try it and see if it works!
     
  11. Offline

    ChipDev

    Pho
    Php ;-;
     
    Bammerbom likes this.
Thread Status:
Not open for further replies.

Share This Page