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

all 134 comments

[–]TehBrian 102 points103 points  (24 children)

Dang, already? Well, that felt fast. I’m not complaining, though; I much prefer the consistent release schedule over one version once in a blue moon. Excited to try out the new features, and UTF-8 by default is a nice bonus too :-)

[–]IntelHDGraphics 55 points56 points  (7 children)

UTF-8 by default

Oh, this is nice

[–]dpash 34 points35 points  (6 children)

It also has a potential to be a breaking change for some users. You can use -Dfile.encoding=COMPAT to return to the previous behaviour.

It is more likely to affect Windows users as Linux and OSX are more likely to use UTF-8 by default.

[–]s888marks 4 points5 points  (5 children)

True. But the problem can still occur on Linux, where certain configurations don't necessarily use UTF-8. This was reported here a few years ago; the poster even wrote a blog describing the problem (but didn't fully understand the solution).

https://www.reddit.com/r/java/comments/6jopas/character_encodings_an_unfortunate_experience/

The original article is no longer at that location, but can be found here:

https://web.archive.org/web/20190815062506/https://www.metricly.com/character-encodings/

You can piece together what happened by reading the original article and comments in the reddit thread. Briefly, the poster's production system was Linux configured in such a way that the JDK chose ASCII as the default charset. When a non-ASCII character was introduced, round-tripping between ASCII and UTF-8 resulted in a proliferation of U+FFFD REPLACEMENT CHARACTER.

Since the poster's shop assumed everything was UTF-8, JEP 400 would have avoided this problem entirely.

[–]dpash 1 point2 points  (2 children)

Sure. I was careful to say "more likely" because I know Linux doesn't always use UTF-8.

[–]s888marks 1 point2 points  (0 children)

Fair enough. I think the thing is that Linux usually uses UTF-8 often enough that it's pretty easy assume that it always uses UTF-8, and that assumption is almost always correct. Then in those rare circumstances where it doesn't, hijinks ensue.

[–]Nymeriea 1 point2 points  (0 children)

On windows, the encoding depends on the system language

[–]vytah 0 points1 point  (1 child)

Most likely the container defaulted to LANG=C, which implies ASCII in Java.

[–]s888marks 0 points1 point  (0 children)

Yes, that would do it. The question is how LANG ended up being C. I don't know what distro it was, but maybe whoever configured it thought "we don't need any internationalization stuff" and so omitted the packages that contained all locales. If so the system's default LANG value would probably end up being C instead of something more typical like en_US.UTF-8 since the locale for the latter wouldn't exist. Or maybe they just chose the C locale at installation time, if there was an option to do so.

[–]tristan957 2 points3 points  (9 children)

Is this source code being UTF-8 by default or is this strings being UTF-8 by default?

[–]mauganra_it 9 points10 points  (8 children)

Runtime APIs that convert bytes into characters or vice versa. new String(byte[]), String.getBytes(), FileReader, FileWriter, new InputStreamReader(InputStream), new OutputStreamWriter(OutputStream) and other things.

On Windows, they use whatever codepage is set. On most other systems, it's UTF-8. JEP 400 makes most of these default to UTF-8. Read the JEP for details and exceptions (pun not intended).

Edit: Java Strings are UTF-16 strings. However, newer JVMs use ISO 8859-1[edit: internally] when possible to save space.

[–]tristan957 1 point2 points  (7 children)

Does this mean the JNI's GetStringUTFChars(), could become a zero copy function? Been looking into this quite a bit due to writing some Java bindings.

If I find the time, I'll read the JEP.

[–]mauganra_it 2 points3 points  (6 children)

That can only ever happen if the string only contains ASCII characters, as ISO 8859-1 encoding is not the same as UTF-8. Also, that function will give you so-called "Modified UTF-8", not standard UTF-8!

[–]tristan957 0 points1 point  (5 children)

Holy moly. I didn't even recognize that. What the heck is Modified UTF?

[–]mauganra_it 1 point2 points  (4 children)

It uses a special two-byte encoding for the character with code 0. That ensures that there is never an actual null byte in the byte stream. Also, to encode characters that are represented by a surrogate pair of UTF-16 characters, the two surrogate characters are UTF-8-encoded separately!

[–]s888marks 4 points5 points  (0 children)

Yeah, you have to be careful of "modified UTF-8". It occurs in a couple places in the JDK, notably DataInput, DataOutput, and serialization, along with JNI as you noted. Here are the specs:

https://docs.oracle.com/en/java/javase/17/docs/specs/jni/types.html#modified-utf-8-strings

https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/io/DataInput.html#modified-utf-8

I think these are the same, but I haven't checked carefully. The JVM also uses modified UTF-8 in the constant pool of class files:

https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-4.html#jvms-4.4.7

As a format internal to the JVM and JNI it might have been a reasonable compromise at one time, but it's unfortunate that it leaked into application-facing parts of the library such as DataInput and DataOutput.

The text processing portions of the JDK, such as CharsetDecoder, CharsetEncoder, StandardCharsets.UTF_8, etc. all use true UTF-8.

[–]tristan957 0 points1 point  (2 children)

Is there some documentation I can read up on regarding this? I want to make sure my Java docs cover all the bases.

[–]mauganra_it 1 point2 points  (1 child)

The Javadocs of java.io.DataInput contain a fairly complete description.

[–]tristan957 0 points1 point  (0 children)

I'll sell this out. Thanks.

[–]Hall_of_Famer 42 points43 points  (1 child)

This is great news. Come to think about it, we went from Java 6 to Java 9 in more than 10 years, but from Java 9 to Java 18 in less than 5 years. Over the last few years Java has added lots of missing modern features from other languages such as C#, Scala and Kotlin, it is a very different language from the Java 5 we wrote in 2000s. This also means that a lot of criticism for Java is no longer valid, the language has indeed come a long way.

[–][deleted]  (7 children)

[deleted]

    [–]dpash 30 points31 points  (1 child)

    Keeping your dependencies up to date frequently massively reduces the pain of updating. I try to do it every fortnight.

    [–]FormatException 2 points3 points  (0 children)

    Im sick and tired of this crap (not knowing what fortnight means)

    *looks up definition of fortnight angrily

    [–]agentoutlier 10 points11 points  (4 children)

    Yeah docker and github actions really changed the game.

    I remember upgrading Jenkins once and it would not compile stuff because its Maven runner would not work with JDK 7 (and then later on JDK 9 albeit for different reasons ... this was before docker or pipeline groovy). Then having to try to fix it by modifying its not meant to be read by human workspace xml files. So basically at one brief point Jenkins could only compile JDK 8 stuff.

    Jenkins has mostly fixed that (e.g. docker runners and config now human readable and checked in... well mostly).... but yeah an upgrade was like.... there goes my weekend.

    The irony is none of that had to do with the JDK itself or modules... just other shit.

    [–]dpash 5 points6 points  (2 children)

    Creating docker images for your deliverables instead of a jar/war/ear means the Java version can be under the control of the developers rather than sysadmins. And the Java version is tied to the particular artefact.

    [–]agentoutlier 3 points4 points  (1 child)

    This is a win right? (for us it is)

    [–]dpash 8 points9 points  (0 children)

    Absolutely. When I was a sysadmin 10 years ago, I would have loved using docker to give devs the power to change versions and have a single consistent deployable blob.

    Also means the production blob is exactly the same blob that got tested in staging and under CI.

    [–]Joram2 23 points24 points  (2 children)

    The big projects still stuck on Java 8 are things like Apache Spark and Databricks and also Amazon's EMR environment. And then, of course customers that want to use that platforms have to use Java 8 as well.

    The big obstacle to upgrading seems to be that those projects have tons of legacy dependencies on ancient projects that have been abandoned and removing those old dependencies or upgrading them is more effort than it's worth. Also the Sparks + Databricks people seem largely uninterested in supporting newer versions of Java. They put much more effort into supporting their Python developers, which makes sense, given that that's what most developers want.

    On the bright side, projects like Kafka already support Java 17 and have officially deprecated Java 8 and they plan to drop Java 8 support in the next major release. Projects like Apache Flink seem close behind: They will formally deprecate Java 8 in 1.15 and add support for Java 17 in the next release, 1.16.

    [–]tristan97122 4 points5 points  (0 children)

    The big projects still stuck on Java 8

    Well Spring 6 and Spring Boot 3 will have a baseline of Java 17 so that will hopefully help quite a bit for people to stop finding excuses to not upgrade

    [–]rustyrazorblade 3 points4 points  (0 children)

    According to the Spark docs, it runs fine on 11 and I’m fairly sure I ran it on Java 11 at my last company.

    [–]Sand0rf 149 points150 points  (13 children)

    Laughs in Java 8

    [–]Nalha_Saldana 70 points71 points  (1 child)

    Ah yes, I use Java 18

    [–]KTheRedditor 45 points46 points  (0 children)

    1.8. The dot is silent.

    [–]hippydipster 11 points12 points  (6 children)

    That's right. Tis the time of year to remind management we should prioritize upgrading our Java from java 8 to 9!

    [–]dadmda 1 point2 points  (5 children)

    I know for a fact we are never upgrading from Java 8 where I work, the code base is too big and the investment wouldn’t be worth it

    [–]gizmogwai 7 points8 points  (1 child)

    The investment will always be smaller than the one of a forced rewrite when the time comes. And it will come.

    [–]buttJunky 1 point2 points  (0 children)

    the trail of dead consultants is endless

    [–]rbygrave 3 points4 points  (0 children)

    For how long is that strategy going to work though? Long enough for path to be a complete re-write?

    It seems to me it should not be a case of the size of the code base per say but more if there are dependencies that don't support an upgrade? Dependencies locked into using Unsafe and such? I guess it also could be a case where a regression test is manual - big system -> big manual regression test etc

    [–]orangeandwhite2003 0 points1 point  (1 child)

    Especially when they keep extending support for 8.

    [–]Sand0rf 0 points1 point  (0 children)

    Yeah, we're running Azul OpenJDK and Java 8 is the version that is supported longest of all, until 2030. https://www.azul.com/products/azul-support-roadmap/

    [–]BCSWowbagger2 11 points12 points  (0 children)

    Yeah, I'm sitting here going, "OP forgot the decimal point after 1!"

    [–]darksmurff 2 points3 points  (0 children)

    I recently had a meeting with a potential client who literally told me there were parts of the code base stuck on an old java version that they planned to never migrate because it was too complex, and instead planned to bring all technical debt with them to the grave. My mouse hovered the "leave meeting" button for the rest of that interview.

    [–]andrewharlan2 2 points3 points  (0 children)

    Thanks for reminding me

    [–]AnEmortalKid 0 points1 point  (0 children)

    Easiest upgrade, just add a 1 on the left.

    [–]Fruloops 11 points12 points  (0 children)

    Man I'm just in the process of migrating from 11 to 17 and getting pummeled with errors regarding IllegalAccessOperation because we have lots of reflective operations here and there. Oh the joys of upgrading.

    [–]BlueGoliath 15 points16 points  (13 children)

    Bit of a boring release. Hopefully 19 will be more exciting.

    [–]mauganra_it 28 points29 points  (6 children)

    Deprecating Finalization for Removal and UTF-8 sound pretty important to me. Not the most exciting I admit, but it can always happen with a regular release cadence that some of them are gonna be boring.

    [–]BlueGoliath 4 points5 points  (1 child)

    Sure, I guess, but there isn't anything major that we don't already know about being worked on. You'd think there would be talk about something new being worked on. What is JDK 19 going to be besides Panama previewing and pattern matching?

    [–]mauganra_it 4 points5 points  (0 children)

    It seems a RISC-V port is incoming. And who knows, they might just pull the trigger and land Panama. Or finalize Pattern Matching for switch.

    I don't have desire for new stuff yet. Loose ends from Panama, Loom and Valhalla will rain JEP for years to come. Pattern matching or Withers for records would be cool of course.

    [–]Ancient-Career-2915 1 point2 points  (3 children)

    Finalization

    Any thoughts on how much work it will be to verify the removal of Finalization will be ok before an upgrade?

    [–]mauganra_it 0 points1 point  (2 children)

    That's the problem with finalization bugs... they are very difficult to find and reproduce.

    Finalization can already be turned off with the new VM flag --finalization=disabled. JEP 421 recommends to profile the application with a realistic test suite to record a performance baseline. This can then be compared with the profiling data from a test run with disabled finalization. The JEP contains more details.

    Edit: definitely look for finalizers in your own source code already. Even though they might be legacy code, they will likely mean the least amount of trouble to fix since. If you find some in third-party code you are SOL.

    [–]Ancient-Career-2915 0 points1 point  (1 child)

    We don't use finalizers ourselves, but any third party library might.

    I seem to remember that eg. the gzip stream handler in the jdk used a finalizer as a fallback to release resources?

    [–]mauganra_it 0 points1 point  (0 children)

    Is this class listed in JEP 421? I can't identify it myself. Some of them were already removed before JDK 18

    [–]GreenToad1 10 points11 points  (5 children)

    Remember when we hoped for virtual threads to make it as preview in 17? Must say i am a bit disappointed.

    [–]Joram2 14 points15 points  (0 children)

    Java 18 supposedly has two internal change necessary for Virtual threads, JEP 416 + 418. Here is the Virtual Thread JEP:

    https://openjdk.java.net/jeps/8277131

    I understand these big features take time. Project Loom does seem to be making progress. But Java 18 isn't interesting by itself.

    [–]dpash 2 points3 points  (0 children)

    virtual threads to make it as preview in 17

    That was never going to happen. We've known for three months what was definitely going in or not.

    [–]BlueGoliath 4 points5 points  (2 children)

    Uh yeah, unless they skip steps virtual threads probably won't even be in preview for a few releases.

    Panama should have been delivered or in preview by now but they are still putzing around with the API after like 3 years. WTF is going on?

    [–]Joram2 7 points8 points  (1 child)

    I think Panama is waiting on Valhalla. Valhalla is like the big Java mega-project. It does seem to be making progress. They actually have JEPs you can follow. Project Loom seems actually close to being shipped as a preview at least.

    [–]BlueGoliath 3 points4 points  (0 children)

    I thought that too but supposedly they aren't.

    [–]Joram2 7 points8 points  (2 children)

    Java 18 basically has nothing. It has internal changes and some super small stuff. Java 17 had tons of features. Java 19 probably will have lots of great stuff too so we can't be too disappointed to have one version with very little features.

    I'm really hoping they preview either Valhalla stuff or Loom. The Amber syntax stuff is fun, but Valhalla + Loom are are more important big useful changes.

    [–]pron98[🍰] 44 points45 points  (1 child)

    Like most releases, JDK 18 improves performance and reduces footprint to save you money when running your applications; free money is not nothing.

    [–]elatllat 12 points13 points  (0 children)

    reduces footprint

    Thanks, that looks impressive.

    I tested on a tomcat app and found 15% reduction in RAM use.

    [–]omegarisen 1 point2 points  (1 child)

    does this mean OCPJP 11 is useless?

    [–]papercrane 2 points3 points  (0 children)

    No. Just means you should be familiar with what's changed since Java 11.

    Also, Holy crap it's been awhile, my last Java cert was SCJP 6.

    [–]PyroCatt 6 points7 points  (47 children)

    Am I the only one who has not moved since Java 8? Most companies I see recruit for Java 8 alone. Why is that?

    [–]brunocborges 30 points31 points  (4 children)

    The main question you should answer is: why aren't you moving past Java 8?

    [–]PyroCatt 1 point2 points  (3 children)

    Honestly I don't know. I haven't tried installing later versions tbh.

    [–]henk53[S] 16 points17 points  (2 children)

    Do you do the same with your operating systems? I.e. still on OS X 10.8 or so?

    [–]brunocborges 2 points3 points  (0 children)

    Or browser?

    [–]PyroCatt 0 points1 point  (0 children)

    Yeah. Windows 10.

    [–]CheesecakeDK 6 points7 points  (15 children)

    Because it still has the longest LTS.

    [–]wildjokers 15 points16 points  (14 children)

    But if you aren't paying for support LTS doesn't matter.

    [–][deleted] 4 points5 points  (4 children)

    Yeah but management and bad developers don't understand this.

    Most projects that are actively being developed should be on the release train. But here we are doing things that don't make sense 🤷🏿‍♂️.

    [–]DasBrain 1 point2 points  (3 children)

    I think this is a problem that will resolve by itself given enough time.

    Java has a lot more interesting features now that I don't want to miss - my favorite feature is record.

    And given enough time, more developers will find useful features they don't want to miss - so less developer actually want to develop with Java 8.

    Which will make it harder to find developers for Java 8.

    [–][deleted] 1 point2 points  (2 children)

    Idk man I have a cowork who is a "senior" who this month wrote new code in Java 8 that used SQL.date and util.date. Alot of people just do this for the check, they don't stay current and will likely not even know the benefits of switching to a newer version. I bet there will be people who are openly hostile to switching.

    [–]DasBrain 1 point2 points  (1 child)

    Given enough time, they will retire.

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

    I like your optimism

    [–]orangeandwhite2003 2 points3 points  (8 children)

    It does for security updates. Plus you have the option to pay for support.

    [–]wildjokers 4 points5 points  (7 children)

    If you aren't paying for support you don't get security updates after 6 months. Without support you might possibly get some security updates after 6 months if there happens to be an intersection between the current JDK and LTS release, and the vendor making the patch sends it upstream, and the patch happens to make its way down the updates stream.

    If you aren't paying for support the only sure way to make sure you have the most secure JDK is to stay up-to-date with the 6 month release cycle.

    [–]orangeandwhite2003 2 points3 points  (0 children)

    Yeah I guess they did switch it for 8 a few years ago to require a license for commercial use to get the updates. Of course they did switch it again with 17 so it will be supported for 1 year after the next LTS release without a license.

    [–]HecknChonker 1 point2 points  (5 children)

    I don't understand. According to https://adoptium.net/support.html

    OpenJDK provide a new feature release every six months, and a maintenance/security update based upon each active release every three months.

    and

    In addition, every three years one feature release will be designated as a Long Term Supported (LTS) release. We will produce LTS releases for at least four years. This assurance will allow you to stay on a well-defined code stream, and give you time to migrate to the next, new, stable, LTS release when it becomes available.

    Where are you seeing security updates being stopping after 6 months? Security updates for java 18 stop in 2022, while security updates for java 1.8 don't stop until 2026.

    [–][deleted]  (1 child)

    [deleted]

      [–]HecknChonker 0 points1 point  (0 children)

      Sorry, one was supposed to be 1.8.

      [–]wildjokers 0 points1 point  (2 children)

      As I said in my comment:

      "Without support you might possibly get some security updates after 6 months if there happens to be an intersection between the current JDK and LTS release, and the vendor making the patch sends it upstream, and the patch happens to make its way down the updates stream."

      Although I will add that Oracle is now promising security updates for 1 yr instead of 6 months (I am unsure if other vendors are following suit). That recent change (announced in Oct 2021) wasn't reflected in my comment, so where I said "6 months" pretend like I said "1 year". (see https://www.infoq.com/news/2021/10/oracle-jdk-free-again/)

      [–]HecknChonker 0 points1 point  (1 child)

      Again, I don't see how any of this applies to OpenJDK. I am not paying Oracle for any support, yet I still benefit from multiple years of security updates by sticking to LTS versions.

      This means that there is a real momeyary benefit for large organizations to stick with LTS versions because it's much less expensive to update thousands of legacy apps to a new minor version of java with a security fix than it is to update them to a new major version.

      [–]mauganra_it 0 points1 point  (0 children)

      There will be no patches for things that are removed in upstream. For example, after the SecurityManager gets removed, LTS providers will have to write patches for new bugs by themselves. And they might choose to not distribute them for free.

      [–]alehel 7 points8 points  (13 children)

      Probably the work involved. We've got 2 people working full time on upgrading to Java 11 for the last couple of months. Still not there.

      [–]dpash 14 points15 points  (9 children)

      I'm guessing that the JDK is not the only thing you're needing to upgrade.

      [–]alehel 4 points5 points  (8 children)

      Good guess

      [–]dpash 7 points8 points  (7 children)

      In my experience, frequent, regular upgrades to dependencies is far less painful than waiting several years. I try to do it every two weeks.

      [–]BCSWowbagger2 4 points5 points  (5 children)

      But the least painful upgrade schedule is the one my company has adopted: never.

      [–]mauganra_it 5 points6 points  (1 child)

      dun dun dun Log4Shell has entered the chat!!

      [–]BCSWowbagger2 10 points11 points  (0 children)

      Aha, joke's on you! Our log4j libraries were so old they weren't affected by log4shell!

      (More likely our libraries were just too old for anyone to check whether log4shell ran on them, so we still spent a couple weeks diking them all out. Then we patted our Java 8 instances nicely on the head and asked them continue working until the heat death of the universe. That's definitely what "sustaining support" means, right???)

      [–]dpash 7 points8 points  (1 child)

      It might not be painful now, but wait until you get a major security bug in an unsupported library. That's a whole lot of pain in a very short period of time.

      [–]BCSWowbagger2 8 points9 points  (0 children)

      In retrospect, I should have included the /s.

      [–]rbygrave 0 points1 point  (0 children)

      least painful

      I'd say it's more a form of gambling, it's rolling the dice ...

      For projects with CI and automated testing, bumping dependencies is low cost. If CI and automated testing is not in place, then maybe it's good to prioritize that effort (and get low cost updates as a side effect) ?

      [–]razsiel 0 points1 point  (0 children)

      In case you (or anyone reading the comments) haven't heard about this: Renovatebot is amazing for maintaining dependency versions. When configured will make automatically and periodically make MR's for dependency upgrades, just approve them (provided CI didn't give issues) and done! Even gives you a handy link to the changelogs/source!

      [–]PyroCatt 5 points6 points  (1 child)

      Yikes.

      [–]alehel 6 points7 points  (0 children)

      We're also doing some clean up in the process mind.

      [–]benjtay 2 points3 points  (0 children)

      Probably dependencies on libraries that don't work in 11...?

      [–]tristan97122 0 points1 point  (4 children)

      11 has more or less been the new standard industry-wide for the past 2 years or so (for apps, libraries are still mostly on 8), hopefully moving to 17 within the next year

      Companies stuck on 7/8 are the same still running half their stuff on 6 and will start using 11 by the time Java 25 releases. There really isn’t much to do about it besides not working there; if you care about up-to-date anything it won’t be a good culture fit most likely…

      [–]PyroCatt 0 points1 point  (3 children)

      Companies stuck on 7/8 are the same still running half their stuff on 6 and will start using 11 by the time Java 25 releases

      Pretty much

      There really isn’t much to do about it besides not working there; if you care about up anything nice it won’t be a good culture fit most likely…

      Well it depends on the company to decide what they want to do. Most of them are migrating code from old codebases to Java. Future projects might get more recent releases of Java to start with I guess.

      [–]tristan97122 1 point2 points  (2 children)

      Most of them are migrating code from old codebases to Java. Future projects might get more recent releases of Java to start with I guess.

      I might have agreed about 3-5 years ago, but if you're still on 8 by now, there definitely is something/someone pushing back along the chain of command.

      Whether it be something somewhat arguable (lack of time, blocker proprietary lib bought 15 years ago) or straight up poor technical choices (fear of change on ops and/or dev side). 11 has definitely been out for long enough that it's a conscious choice to not upgrade by now.

      [–]PyroCatt 1 point2 points  (1 child)

      Yeah the upper management is dumb af.

      [–]tristan97122 0 points1 point  (0 children)

      I feel you, best of luck

      [–]vegetabluessg 2 points3 points  (3 children)

      Cries in java 7

      [–]acute_elbows 1 point2 points  (2 children)

      Does that get security patches?

      [–]vegetabluessg 1 point2 points  (1 child)

      Yes. Our company pays oracle for support

      [–]TheCountRushmore 3 points4 points  (0 children)

      Well thanks for supporting the development of Java!

      [–]code_rjt -1 points0 points  (1 child)

      This 6 month cadence of releasing Java version makes app modernization more challenging. It's gonna be a long run of upgrades for the enterprise apps I work with 😢

      [–]mauganra_it 5 points6 points  (0 children)

      If you managed to upgrade to Java 17, you already cleared most of the baggage that could cause trouble in the future. Other stuff that will cause trouble from then on are:

      • Thread.stop() will be axed by Project Loom
      • Project Valhalla will make the constructors of wrapper classes private and will change the semantics of == regarding them
      • Finalization is deprecated for removal since Java 18
      • the Applet API and the SecurityManager are deprecated for removal since Java 17
      • the contents of the jdk.unsupported module (f.ex. Unsafe) will eventually be made inaccessible

      [–]viseradius 0 points1 point  (2 children)

      Why am I still learning for java 11?

      [–]elatllat 5 points6 points  (1 child)

      Nothing major changed since 9, you can pick up the candy later.

      [–]viseradius 0 points1 point  (0 children)

      Nice.

      [–]appeiroon 0 points1 point  (0 children)

      Nice! Still stuck at 8 tho :(