Hacker News new | past | comments | ask | show | jobs | submit login
A simple Minecraft written in Rust (github.com/pistondevelopers)
169 points by gnocchi on Feb 9, 2015 | hide | past | favorite | 59 comments



Last I heard, Hematite was more of a Rust-based renderer of Minecraft worlds than an actual game. For example, I don't think it currently supports construction or destruction of blocks.

Piston, the project that's backing Hematite, is very interesting in that it's an attempt to explore idiomatic approaches to game engine design in Rust. I'm not sure how many game developers will value Rust's memory safety over C++'s sheer flexibility and mature tooling, but I commend the Piston developers for taking a chance on a new and unproven language!

EDIT: If you'd like to take a closer look at game development in Rust, be aware that there exist dedicated communities for this on reddit ( http://www.reddit.com/r/rust_gamedev ) and IRC ( http://client01.chat.mibbit.com/?server=irc.mozilla.org&chan... , a.k.a. #rust-gamdev on irc.mozilla.org).


Also, last time I heard, they were shooting for pixel-perfect rendering, which is really interesting.


What does that mean in this case?


http://imgur.com/a/bHcLD

They're trying to make those two images look exactly the same.


I believe it means that the developers made an effort to exactly mimic Minecraft's lighting and ambient visual effects, rather than simply slapping the textures on the geometry and calling it a day.


> I'm not sure how many game developers will value Rust's memory safety over C++'s sheer flexibility and mature* tooling*

Spot on. What sets an immediate roadblock for me is this, straight from the Piston readme:

> You should be able to run the command rustc -v > You should be able to run the command cargo -V

The first one is incredibly easy: brew install rust, which currently brings in 1.0.0-alpha. The second one, well†... Its very homepage[0] says:

> The easiest way to get Cargo is to get the Rust nightly build

So the only easy step out of two is instantly invalidated. This is followed by:

> This will get you the latest Rust nightly for your platform along with the latest Cargo. You should run this script almost every day to get the latest updates.

Okay so the de facto standard dependency manager is apparently deeply tied to rust itself, yet not included in the rust distro, and it's apparently quite not stable enough to warrant daily updates. And even 1.0.0 (granted, "-alpha") doesn't include it, so it may not be ready before god knows when.

Compare:

    export GOPATH=$(pwd)
    brew install glew
    brew install homebrew/versions/glfw3
    go get github.com/go-gl/gl
    go get github.com/go-gl/glfw3
    vim whatever.go     # https://github.com/go-gl/glfw3#example
    go run whatever.go  # yay OpenGL!
This means that although Piston does look great, and Rust looks promising, it does not look not dependable right now, because it just doesn't feel mature (even if it is, which I don't know, because my time is limited and there were enough explicit or implicit warnings about dragons to turn me away).

† I'm perfectly aware that cargo is available as a Cask, but for various reasons, I don't want casks in my system, and it doesn't preclude the remainder of the discussion.

[0]: http://doc.crates.io


Do you know what alpha means?

or were you just wanting to talk about go?


> Do you know what alpha means?

Precisely†. This is just to give some credence to the "mature" part of the parent comment, beyond language features and alleged "proven-ness". I'm not complaining in any way, just reporting how things are or can be perceived.

The comparison with Go is purely factual: I just happen to have done exactly this in Go, and recollected an account of my experience when I wanted to explore doing the same thing in Rust. If referencing Go has to do with anything, it's laziness as I extracted that from my shell history.

I am deeply sorry if the above post sounded snarky or whatever. This was not the intent.

† BTW, when I did this originally, Rust was 0.9 (no alpha anywhere), which has the virtue of saying squat about what you should expect without a statement of version semantics (e.g node.js is 0.12 and definitely production-ready)


  > Rust was 0.9 (no alpha anywhere), which has the virtue 
  > of saying squat about what you should expect without a 
  > statement of version semantics
Rust has been openly adhering to Semantic Versioning (semver.org) since 0.1. According to the semver specification, any releases with a major version of zero carry no guarantees whatsoever.


Cargo is "ready" enough to be near-universally employed among Rust users, and the packaging story will be nailed down for the 1.0-stable release.


Nice job, now if you can make a simple Rust in Minecraft I'll be even more impressed!


Rust program compiled for a redstone computer running on Hematite, which in turn has been compiled for a redstone computer running on Hematite.

Recursion at its finest.


Rusty turtles all the way down.


rustic, rusty turtles, even sounds less irritating


I have a question for people who've advanced ahead of me into rust.

With the C and the kind of C++ I write I have some kind of idea what the assembly language that will be generated by the code I'm writing looks like. I have some idea how the data is going to be laid out in memory. You can think of C and good C++ code as a more convenient way of writing the machine instructions that will execute on your cpu, well at least kinda. But you really can't do this with say, haskell or clojure (he said aware he knows little about the former and even less about the latter).

How is rust? Can you write some rust code knowing this datastructure will be laid out like so, knowing this function will likely not thrash cache too badly and so on? Inline asm? If you had the crazy idea of writing the "platinum" linker in rust in an attempt to outperform the (written in c++) gold linker is that doomed before you start. Nothing to choose for efficiency between these languages?

Oh and how's the debugger? Same as gdb on c++?


Being able to have an idea of the assembly that will be generated from Rust code is certainly a motivating goal (I believe that pcwalton has previously indicated his ability to divine as much), though I personally have no experience with bare assembly so I cannot attest to it myself.

You can certainly guarantee data layout (such is required for C interop, for which Rust has very nice support), though by default Rust will e.g. reorder a struct's fields in order to optimize its representation (taking into account padding and such) unless you explicitly ask it not to.

Rust also tries to favor explicitness where it can. For example, even though many people have requested C#-style properties where `foo.bar = baz` can implicitly call a setter function, Rust has rejected this because the runtime behavior of a function call is too different from the runtime behavior of a field lookup. As another example, anything that looks like `foo.bar()` will always be a method call, and `foo.bar` (without direct parentheses) will always be a field lookup, so if you want to call a function pointer stored in a struct field you need to write `(foo.bar)()`.

There are also places where Rust is more explicit than C++. For example, a C++ function `void bar(int& foo)` can be called like `bar(6)`, but the analogous Rust function `fn bar(foo: &i32)` must be called like `bar(&6)`.

However, Rust also has places where it is less explicit than C++. For example, whereas C++ has both `bar.foo()` for direct method calls and `bar->foo()` for method calls that are invoked through a deref, Rust has only `bar.foo()` which has the capability to dereference `bar` if necessary. So it would be incorrect to say that Rust is ultimately more or less explicit than C++... it just has a different mix of explicit and implicit operations.

As to the rest, Rust does indeed have inline asm, and I hear that Rust produces DWARF debuginfo which allows it to be debugged via gdb (though it has to pretend to be C++ in order to play nicely with existing tooling, which can make it awkward sometimes).


Nice answer, thanks!


Lots of other examples here too https://github.com/PistonDevelopers and website available here http://www.piston.rs/ for those who want to get started with piston


Note that Hematite uses gfx-rs for rendering, it was mentioned on HN earlier:

https://news.ycombinator.com/item?id=8610459


Build appears as broken but it should be working, Travis is doing its thing. EDIT: Green Travis again :)


I don't feel like submitting a PR for it, but you have one syntax error and one suboptimal piece of syntax in the readme at the bottom:

The Windows section should read:

    Minecraft folder: %appdata%\minecraft
    Worlds folder: %appdata%\minecraft\saves\<world>
(observe the "closing" percent sign)

and the OSX one could read:

    Minecraft folder: $HOME/Library/Application Support/minecraft
    Worlds folder: $HOME/Library/Application Support/minecraft/saves/<world>
or, of course, using the same tilde syntax as in Linux.


Fixed! Thanks


Something I always wonder for games not made with C++: how is the performance?


Note: Rust is designed to be as efficient as C++. Well, sort of, since we don't have all of the optimizations yet (we use llvm so we get most of them for free)

But Minecraft itself is in Java. And it does get pretty slow even with low settings as compared to other games on high settings.


Rust uses bounds checking in safe code, and despite idiomatic Rust allowing many bounds checks to be optimized out (eg. iterators instead of C style for loops), bounds checks are not free. It's possible that things like stricter aliasing rules in Rust will allow more optimizations and make up the speed difference, but the design of Rust does not guarantee the possibility of matching C++ performance unless you use unsafe code.


On the other hand, you have no choice but to use unsafe code in C++; that is, there's no line between code that is guaranteed to be memory safe and code that risks corruption and undefined behaviour.

In any case, the route people will hopefully take is to write the code in the obvious way, with bounds checks and all, and profile. Functions which take significant amounts of time can then be optimised more manually (and, bounds checks usually aren't the bottleneck).


At some point the performance margin grows so close that it's irrelevant to almost any application. IMO, it's already there for a broad swath of application classes. For those inner-most-loop, optimizer-has-failed-me cases, Rust can still fall back on unsafe code.

I'll note that this kind of skepticism has been going on since John Backus[1] was heckled over using (ahem, "wasting") then-precious CPU time on compiling code using the first versions of FORTRAN. His detractors (and everyone else) quickly learned the value of conserving programmer time vs. CPU time.

[1] https://en.wikipedia.org/wiki/John_Backus


Games are one of the areas where it is a bit foggier as your rendering loop is incredibly sensitive to performance problems.

You have a ton of work to do and cannot take more than 16.7ms (60Hz) to do it in period. Heck given the recent move some have made for 144Hz monitors it leaves you with less than half the time (6.9ms).

Not to say that C++ is the only way to pull it off, just that games are a soft-real-time system which therefore have stricter requirements for performance.


Most inner loops in game code are iterating over collections, whether it be lists of actors, lists of vertices, skeletal animation data, or what have you. Rust does not incur bounds check overhead for iteration.


In my experience not being able to use iterators is rare in most Rust code. Inner loops that have array lookups are overwhelmingly iterating over collections. It's much more common to have to drop down to unsafe code because you need to implement some low-level data structure that Rust's ownership rules can't yet prove safe.

I have personally never seen the bounds checks be a problem except in microbenchmark-like things, and those are almost always compiler bugs involving missed optimizations that the compiler could have done but does not presently. I think "the design of Rust does not guarantee the possibility of matching C++ performance unless you use unsafe code" is too strong a statement.


> Rust does not guarantee the possibility of matching C++ performance unless you use unsafe code.

As with any performance question, this is hard to actually say in general. In the Benchmarks game(1), there are several benchmarks where Rust has no unsafe code and is still faster than C.

1: personal note: ugh, all usual caveats apply, etc.


If you think there's something wrong about the benchmarks game then don't point to the benchmarks game as evidence for what you wish to be true.


Bounds checks are obviously not free in theory, but I've heard multiple stories of people who did the measurements on their codebase, and concluded that the cost was so tiny as to be statistically unmeasurable, compared to other noise in run-time measurements. Here's one:

https://www.reddit.com/r/rust/comments/2nlis8/which_classes_...


It's not just a matter of optimizations, there are some things you can do in C++ that incur extra pointer indirections in Rust because you don't have move constructors.


Can you give an example?


Any time you would have backpointers that need to get updated when the pointee object moves. One example would be a pair of noncopyable promise send/receive objects, each with a pointer to the other. Or just one end could be copyable. Or take green threads single-core mutexes and their acquirers. The acquirers should be able to be moved around without getting "lost".


Though in this case it makes sense to compare it to the "reference implementation" in Java. Regular Minecraft has noticeable GC pauses in my opinion.


Hematite doesn't implement anything near to the full game, so it wouldn't be a fair comparison. It's mostly just a world visualizer, sort of like Overseer is for Dwarf Fortress ( http://www.bay12forums.com/smf/index.php?topic=63484 ).

And although it would probably make a good jumping-off point for anyone who wanted to develop a Minecraft clone, the original goal of Hematite was only to serve as a proof-of-concept of rendering 3D graphics in Rust.


Are you sure they are caused by GC? I'm asking because GC is an easy "culprit" and when a Java app pauses / jitters, many people immediately blame it on GC without any hard evidence. While in fact pauses / unresponsiveness can be caused by many other reasons, not specific to JVM (e.g. improper thread locking, improper use of dynamic data structures, doing hard work on the GUI thread, etc.). Actually, a well coded game should not use dynamic memory allocation in the game loop, neither in Java nor in C++.


I've seen Minecraft generate 400Mb/min worth of garbage and then GC with a noticable stutter.

The most egregious stutter though is when Minecraft generates new chunks.

Minecraft is not a well-coded game. ;)


If you're looking to play a open source minecraft-like game, without worrying about microsoft copyrights on world files, http://minetest.net/ is a lot of fun.


> microsoft copyrights on world files

If that were something to worry about, then wouldn't OpenOffice, LibreOffice, iWork, Google Docs, etc. all be in legal trouble right now for the same thing with .doc files?


Nope because MS doesn't create .doc files, they just make the software that saves/loads them. The issue in this case is that the Minecraft textures and item geometry are created by MS (by way of Mojang) and hence MS has the copyright on them. If you replace the textures with alternative ones created by other people you're probably in the clear so far as the textures go, but the NPCs and objects geometry data is still covered.


But the world files are just the save files. As far as I know, there are no texture or object data in the saves.


Definitely true. Plus there's a long standing precedent that save files aren't copyrightable, since they aren't a derivative work.


One could maybe make the case that Minecraft's terrain generation would make your world a derivative work, but if you're building in superflat than definitely agreed.

I have no idea about the former, but if MS had any claim to a world built from scratch then Adobe would own everything ever made in Photoshop and Autodesk would own pretty much all video game content since the late 90s.


Now write the game "Rust" in Minecraft.


I hope Microsoft plays nice with these clones in the future. Especially when they use official assets.


How is that dependency graph plotted in the README?



Yep, I wrote that as my first project in rust and it was used for a lot of the Piston project repos. Unfortunately I stopped updating that in December because breaking changes were happening too frequently for me to keep up.

I'll probably revisit it soon now that 1.0 is fairly imminent, but prs are welcome of course!


I'm not associated with the project, but it looks like graphviz (http://graphviz.org/) to me.

graphiz is amazing for making graphs. My favorite interface for it is dorothy (https://github.com/daveray/dorothy).


Graphviz is ok for quickly plotting a dependency graph. I have yet to see a beautiful example of auto-layouted graphs. Beautiful means getting the details right like lines not crossing at narrow angles, drawing a little "hop" when lines cross, and not being restricted to strict rows like dot.


Next up: A simple Rust written in Rust.


Isnt that just rust? Rust is written in rust...


I assume the joke is that "Rust" is the name of a video game inspired by Minecraft (and other games): http://playrust.com/


You assume right!


But I wouldn’t call it simple. rustc is a couple hundred thousand lines of code, split across several libraries. librustc_borrowck alone is almost six thousand lines.


First person to say something like "This isn't minecraft, minecraft is way more than just placing blocks!" loses.




Consider applying for YC's first-ever Fall batch! Applications are open till Aug 27.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: