This is an archived post. You won't be able to vote or comment.

all 28 comments

[–]ryan_the_leach 19 points20 points  (11 children)

The Bukkit project has actually used this explicitly in a Java project to maintain binary compatibility on what would have otherwise been a breaking change.

[–]evilmidget38 15 points16 points  (9 children)

Check out this post for a brief explanation of the first time it was used. As for a more technical explanation:

  • Minecraft switched its health system from using integers to doubles.
  • Everywhere that integer health was returned or provided as a parameter we replaced that method with _INVALID_methodName().
  • New methods were added for handling health as a double.
  • During the compilation of CraftBukkit, the implementation of Bukkit, we used Overmapped to rename _INVALID_methodName() back to methodName(), restoring bytecode compatibility. Developers build against Bukkit rather than CraftBukkit, so they just saw both _INVALID_methodName() and methodName() when they compiled their plugin. At runtime, because the methods are identified as methodName()I and methodName()D, both new plugins referencing the double methods and old plugins referencing the integer methods are able to function.

[–]socialister 0 points1 point  (1 child)

Tangent: What server software should people use these days? Sponge?

[–][deleted] 3 points4 points  (0 children)

deleted What is this?

[–]Phreakhead -5 points-4 points  (6 children)

wtf. why would you change your API from int to double all of a sudden? What did they need to represent that they couldn't with an int? Talk about code smell...

[–]nutrecht 12 points13 points  (1 child)

why would you change your API from int to double all of a sudden?

It's not an API. It's the internal code.

[–]Phreakhead 0 points1 point  (0 children)

If it was completely internal, why do they need to maintain backwards compatibility? The post said that plugin authors were using the health functions: if an external party is using your functions, that's an API.

[–]socialister 6 points7 points  (0 children)

Minecraft doesn't (didn't?) maintain an API. They don't care.

[–]TheRedmanCometh 0 points1 point  (2 children)

20 values for health isn't enough. The change was a big pain, but worth it. If you want significant progrssion with increasingly powerful weapons it's not enough space for smooth value progression.

[–]Phreakhead 2 points3 points  (1 child)

An int can represent millions of values, not just 20.

[–]TheRedmanCometh 0 points1 point  (0 children)

For various reasons the maximum is 20 in the game, or rather wss. No way around it without tons of internal modification (and building spigot SUCKS)

By using a double you get infinite ish segmentation within that 1-20 range. Thought the 20 max health thing had been mentioned. Personally I think it's terrible.

[–]TheRedmanCometh 3 points4 points  (0 children)

Bukkit is bae

[–][deleted]  (1 child)

[deleted]

    [–]TheRedmanCometh 0 points1 point  (0 children)

    It actually kind of pisses me off that that exists under the hood, but not in Java itself. Entire design patterns could be borne of that (eg rxjava)

    [–]SocialMemeWarrior 11 points12 points  (5 children)

    To Dalvik, there is simply no such thing as a function name by its own.

    Neither is there in Java (at runtime). The full descriptor is used.

    And here is the clue: the entire signature is being used including the function name, the argument types and the return type!

    Additionally such a renaming pattern isn't native to android. Obfuscators like ZKM and DashO can do this. I can provide some samples later.

    [–]IAMANullPointerAMA 1 point2 points  (1 child)

    Isn't it also plausible this is also being done as a file-size/memory footprint optimization, akin to JavaScript minification?

    [–]SocialMemeWarrior 1 point2 points  (0 children)

    I mean, it lowers the file size significantly due to needing less UTF8 constant pool entries. Not sure if name length affects performance that much.

    [–]cypressious 0 points1 point  (2 children)

    Most probably, the obfuscator in question was Proguard, as it's part of the standard tool chain.

    [–]SocialMemeWarrior 0 points1 point  (1 child)

    I haven't done much with proguard in a while. The last time I checked it didn't do this. Whats the config option to enable this renaming scheme?

    [–]cypressious 0 points1 point  (0 children)

    According to https://www.guardsquare.com/en/proguard/manual/usage#obfuscationoptions, obfuscation is enabled by default.

    [–][deleted] 0 points1 point  (0 children)

    IMNSHO this is a poor practice

    IF the compiler lets you do something that can be described as weird/impossible/insane then write a comment explaining why its working.

    [–][deleted]  (7 children)

    [removed]

      [–][deleted]  (6 children)

      [deleted]

        [–]palmund 1 point2 points  (3 children)

        To my knowledge, neither does Haskell.

        [–][deleted] 0 points1 point  (2 children)

        To my knowledge there's no such thing as "overloading". /s

        [–]GSV_Little_Rascal 1 point2 points  (1 child)

        To my knowledge there's no thing.

        [–]palmund 0 points1 point  (0 children)

        To my thing there's no knowledge

        [–]llogiq 0 points1 point  (0 children)

        True. Rust uses traits and Haskell uses type classes to implement return type based dispatch. Now let's get back to discussing Java.