Solved Overriding Entities (ADVANCED)

Discussion in 'Plugin Development' started by TeeePeee, Nov 21, 2013.

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

    TeeePeee

    Hey guys,

    I've been screwing with custom entity classes with custom pathfinders and whatnot but I've found that it has the tendency to create PermGenSpace errors. I'm assuming this is caused due to a memory leak as I use reflection to register the entity (and replace the default Minecraft entity with it) using a method by the brilliant Jacek (tutorial here)

    I haven't been able to correct this memory leak. On disable, I've tried calling System.gc() to invoke a garbage collection, unregistering the entities on disable and despawning all my overriding entities, etc. etc.

    So my question is this:
    *How can I spawn in custom entities without having to use reflection to get Minecraft to allow it?
    * Alternatively, how can I use the method I'm using but do garbage collection on the overriding class and its pathfinders to prevent memory leaks?

    Bonus points to anyone that can answer:
    * How can I make the overriding entity spawn only in specified worlds?

    I don't know if this is permitted (and pardon my ignorance if it isn't) but I'm going to tag all the brilliant minds I can think of that have dabbled in entity customization in the past and might be able to help.
    bergerkiller Keyle CorrieKay RingOfStorms

    Link to GitHub where all my fancy CustomMobs reside: here
     
  2. Offline

    bob7

    Hmm, how exactly are you spawning your NMS entities? I've never had a memory leak issue when extending/overriding mobs. Maybe you should give RemoteEntitys a go?
     
  3. Offline

    xTrollxDudex

  4. Offline

    ZeusAllMighty11

    You should not be calling System.gc(). The jvm will call garbage collector when it needs to, and by forcibly invoking the method, you could be causing a bunch more issues.
     
    TeeePeee and 1Rogue like this.
  5. Notice that when you register a custom entity to minecraft, you also need to unregister it, else ALL plugins will get affected by a memory leak that YOU created.
     
    ZeusAllMighty11 likes this.
  6. Offline

    Scyntrus

    I've noticed in your code you have this in the constructor of your custom entity.
    Code:java
    1. try {
    2. Field field = PathfinderGoalSelector.class.getDeclaredField("a");
    3. field.setAccessible(true);
    4.  
    5. field.set(goalSelector, new UnsafeList<PathfinderGoalSelector>());
    6. field.set(targetSelector, new UnsafeList<PathfinderGoalSelector>());
    7. } catch (Exception exc) {
    8. exc.printStackTrace();
    9. }

    I'm not sure if this will exactly solve this problem, but this code is inefficient. A new field object is created every time you create a new instance of your entity. Instead, you can have an external class create the field object once (as static), and then access the field method from your entity class.
     
    TeeePeee and ZeusAllMighty11 like this.
  7. Offline

    1Rogue


    That being said, I don't believe that would cause an issue with PermGenSpace

    TeeePeee

    Are you registering static entity instances?
     
    TeeePeee likes this.
  8. Offline

    bergerkiller

    You get permgen errors because you still have (some) of your own Class instances used in your plugin being used as variables on the server. When your plugin enables again later on, your classes are re-loaded, causing a duplicate of that class to be stored. If you want to fix these errors, be sure to unhook all your plugin-added classes. Also, avoid static variables in these classes, as they add up to the permgen space. Even better, set them to null so no memory is lost there. Preferably you would unhook all you possibly can.
     
    TeeePeee likes this.
  9. Offline

    TeeePeee

    bob7
    They spawn naturally after using the CustomEntityType in the link I provided.
    ferrybig
    I realize this and onDisable, I unregister them (see https://github.com/JordanSicherman/MyZ-3.0/blob/master/src/myz/MyZ.java#L268).
    Scyntrus
    Fair enough, thanks! As you said, I don't know if that can be attributed to the PermGenSpace but if the entities aren't being disposed of, it will certainly add to the memory leak.
    1Rogue
    I am, as you can see in my GitHub source, registering the entity class, not an instance of the entity.
    bergerkiller
    I don't believe I store any references to a custom entity anywhere, but I'll take a fine toothed comb to it again. By unhook I assume you mean essentially nullify? Correct me if I'm wrong but shouldn't the ClassLoader dispose of the main class when the plugin is disabled and unhook all static variables, etc. by default?
     
  10. Offline

    1Rogue


    Can you link this github source?
     
  11. Offline

    DarkBladee12

    1Rogue look at the end of his first post, because the source is linked there ;)
     
  12. Offline

    TeeePeee

    As an update, nowhere is it storing any references to a Custom Entity. I also made the change suggested by Scyntrus to not create a field for each entity pathfinder. Still, I get the PermGenSpace error after a few reloads. I also ensured there are no static variables in any Custom Entity Class as suggested by bergerkiller.

    From the articlelinked to by xTrollxDudex:
    This sounds like what is happening. I wish there was a way other than to modify the startup batch file to fix it though. Any more ideas, anyone?

    Thanks for all the replies thus far! You guys have been tremendously helpful.

    [EDIT] Even with the arguments (-XX:+CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabled) from the article quoted above, I still get the same error after the same amount of time.
     
  13. Offline

    xTrollxDudex

    TeeePeee
    Did you modify the batch file? I think there may be a method in System to modify garbage collecting parameters, may.
     
  14. Offline

    TeeePeee

    xTrollxDudex
    As stated above, I did modify the batch file to include the parameters that allegedly 'solve' PermGenSpace errors. The other arguments can create more PermGenSpace, but this will only postpone the inevitable. This memory leak will be the death of me :'( and I am sure it is because of the custom entities as I commented the registering and spawning out and everything ran smoothly (reloaded 40+ times without PermGenSpace error).
     
  15. Offline

    xTrollxDudex

    TeeePeee
    Try running a few commands using Runtime#execute(...)
     
  16. Offline

    TeeePeee

    xTrollxDudex
    Never really used Runtime#exec calls. Care to provide an example for this context? Thanks!
     
  17. Offline

    xTrollxDudex

    TeeePeee
    I'm not really sure what it does :p
     
    maxben34 likes this.
  18. TeeePeee likes this.
  19. Offline

    TeeePeee

    ferrybig
    Sorry, I didn't update my git recently. This is the code I use to unregister the entities I previously register:
    [EDIT] *snip*
    And do so on disable of the plugin. This is effectively the reverse of what registerEntities() does, so I figure it should work, but correct me if I'm wrong.

    [EDIT] Went through the EntityTypes class and realized that calling the method 'a' in reverse (NMS instead of custom) won't cut it. About to write the reflection to remove my custom class from each HashMap.

    [EDIT 2] Found the source of the leak, thanks to ferrybig :) Went through EntityTypes in net.minecraft.server and realized that I never removed the custom class reference in the HashMaps 'c' and 'e' (the others were already overwritten due to my previous method. Here's the before and after unregister (the after one works, before one doesn't):

    Before:
    Code:java
    1. public static void unregisterEntities() {
    2. for (CustomEntityType entity : values())
    3. try {
    4. if (method == null) {
    5. method = EntityTypes.class.getDeclaredMethod("a", new Class<?>[] { Class.class, String.class, int.class });
    6. method.setAccessible(true);
    7. }
    8. method.invoke(null, entity.getNMSClass(), entity.getName(), entity.getID());
    9. } catch (Exception e) {
    10. e.printStackTrace();
    11. }
    12.  
    13. for (BiomeBase biomeBase : BiomeBase.biomes) {
    14. if (biomeBase == null)
    15. break;
    16.  
    17. for (String field : new String[] { "K", "J", "L", "M" })
    18. try {
    19. Field list = BiomeBase.class.getDeclaredField(field);
    20. list.setAccessible(true);
    21. @SuppressWarnings("unchecked")
    22. List<BiomeMeta> mobList = (List<BiomeMeta>) list.get(biomeBase);
    23.  
    24. for (BiomeMeta meta : mobList)
    25. for (CustomEntityType entity : values())
    26. if (entity.getCustomClass().equals(meta.b))
    27. meta.b = entity.getNMSClass();
    28. } catch (Exception e) {
    29. e.printStackTrace();
    30. }
    31. }
    32. }


    After:
    Code:java
    1. public static void unregisterEntities() {
    2. // Remove the custom class from the maps where it is the key, not the value.
    3. for (CustomEntityType entity : values()) {
    4. try {
    5. Field c = EntityTypes.class.getDeclaredField("c");
    6. c.setAccessible(true);
    7. ((Map) c.get(null)).remove(entity.getCustomClass());
    8. } catch (Exception e) {
    9. e.printStackTrace();
    10. }
    11.  
    12. try {
    13. Field e = EntityTypes.class.getDeclaredField("e");
    14. e.setAccessible(true);
    15. ((Map) e.get(null)).remove(entity.getCustomClass());
    16. } catch (Exception e) {
    17. e.printStackTrace();
    18. }
    19. }
    20.  
    21. for (CustomEntityType entity : values())
    22. try {
    23. if (method == null) {
    24. method = EntityTypes.class.getDeclaredMethod("a", new Class<?>[] { Class.class, String.class, int.class });
    25. method.setAccessible(true);
    26. }
    27. method.invoke(null, entity.getNMSClass(), entity.getName(), entity.getID());
    28. } catch (Exception e) {
    29. e.printStackTrace();
    30. }
    31.  
    32. for (BiomeBase biomeBase : BiomeBase.biomes) {
    33. if (biomeBase == null)
    34. break;
    35.  
    36. for (String field : new String[] { "K", "J", "L", "M" })
    37. try {
    38. Field list = BiomeBase.class.getDeclaredField(field);
    39. list.setAccessible(true);
    40. @SuppressWarnings("unchecked")
    41. List<BiomeMeta> mobList = (List<BiomeMeta>) list.get(biomeBase);
    42.  
    43. for (BiomeMeta meta : mobList)
    44. for (CustomEntityType entity : values())
    45. if (entity.getCustomClass().equals(meta.b))
    46. meta.b = entity.getNMSClass();
    47. } catch (Exception e) {
    48. e.printStackTrace();
    49. }
    50. }
    51. }
     
    ferrybig likes this.
Thread Status:
Not open for further replies.

Share This Page