2

Run Your Rust Games in a Browser

 2 years ago
source link: https://medium.com/pragmatic-programmers/run-your-rust-games-in-a-browser-ceea86b04616
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Run Your Rust Games in a Browser

📚 Connect with us. Want to hear what’s new at The Pragmatic Bookshelf? Sign up for our newsletter. You’ll be the first to know about author speaking engagements, books in beta, new books in print, and promo codes that give you discounts of up to 40 percent.

1*mxm1vcVfq5vubqgyNqu22Q.jpeg?q=20
run-your-rust-games-in-a-browser-ceea86b04616
Image by Vectors Bang on Shutterstock

One of the great things about using Rust and bracket-lib is that you can compile your games for the web.

Rust natively supports compiling to WebAssembly (WASM).

With a little bit of automated work to map WebAssembly bindings to native JavaScript, you can run your games in your browser.

🐉 Here’s Flappy Dragon in a Browser (Requires Chrome, Firefox, or Edge).

What Is WebAssembly?

WebAssembly began life as Emscripten, a project to build C++ for the web. Emscripten compiled your C++ programs into a binary format readable by JavaScript. A simple JavaScript run-time then ran your compiled code in the browser. Emscripten could wrap your C/C++ functions in a JavaScript header, allowing you to call high-performance functions from inside JavaScript. You could also use Emscripten to write full games.

Browser developers realized that Emscripten had the potential to extend the capability of browser apps and added back-end code to make WebAssembly run even faster. After a few rounds of development, WebAssembly was formalized as a compilation target. LLVM included it as a back-end, and Rust was one of the first platforms to provide first-class WASM support.

WASM does come with limitations:

  • Threading works differently in a web browser and was largely disabled in response to the Specter line of vulnerabilities. So you can’t take advantage of the multi-threading built into Legion (and similar ECS systems). Your game will run in a single thread.
  • Not every browser runs WASM well — it works great in Chrome and Firefox, but not so well in Safari.
  • Large games can take a while to download.
  • WASM doesn’t have a native filesystem, so loading assets from your web server is difficult. You can alleviate this issue by embedding your assets into your program.
  • You need a web server. The server can be running locally, but most browsers refuse to execute WASM programs loaded locally with file: links.

So with those caveats in mind, let’s get started making your Rust games work in your web browser.

Making Rust Games Work in a Browser

Making Rust Games work in a browser requires several steps. We’ll need to do the following:

  • Update bracket-lib
  • Install the WASM toolchain
  • Install the wasm-bindgen program
  • Compile
  • Embed assets for filesystem dependencies
  • Build and bind
  • Create a webpage

Update Bracket-Lib

The latest version of bracket-lib includes some changes to make WebAssembly programs run better. Update your dependency in Cargo.toml to read:

Now run cargo update to update your crates.

Install the WASM Toolchain

Rust’s cargo system natively supports cross-compilation. Cross-compilation allows you to compile programs for a platform other than the one you are currently using. For example, you can make Linux builds of your game without leaving Windows. WASM is supported as another platform, so you cross-compile your programs into WASM format. The official name of the WASM platform is: wasm32-unknown-unknown.

The Rust install tool (rustup) can install toolchains for you. Install wasm support on your computer with the following command:

rustup target add wasm32-unknown-unknown

Rust will download and install the current wasm32-unknown-unknown toolchain support for you.

Install Bindgen

Compiling to WebAssembly isn’t quite enough to make your programs work in your browser. You also need some JavaScript to connect your WASM program to the browser’s execution engine. You could create this by hand, but it’s really tedious. Instead, the wasm-bindgen program can do the work for you.

Note that when you update your Rust setup, you’ll want to repeat this process to get the newest wasm-bindgen tool.

You can install bindgen with the following command:

cargo install wasm-bindgen-cli

Compile Your Programs

You can now compile your programs to WASM with the following command:

cargo build --target wasm32-unknown-unknown --release

This step will download all of your dependencies, and compile them — alongside your program — into WASM format. The binaries will be optimized and almost ready to use. You still have a couple of steps remaining: handling filesystem dependencies and making some HTML to actually run your program in a browser.

Embed Assets for Filesystem Dependencies

WASM doesn’t provide direct support for your filesystem. Overall, that’s a good thing — you don’t want random programs on the Internet gaining access to your private files! It does make life a little more difficult when you need to load resources to support your program. bracket-lib helps get around this by providing a kind of embedding system: you can embed resources directly into your program, including them at compilation time. This process makes compilation take longer (and produces larger binaries), but it removes the need to create a web-based asset loading system.

bracket-lib automatically embeds terminal8x8 and vga8x16 fonts for you. The basic bracket-lib demos will work unchanged because of this, as will the ASCII version of Flappy Dragon. However, you probably need more than the built-in font files! You can add fonts with a two-step process.

In your main.rs file, outside of the main function, you can embed resources with the embedded_resource! macro provided by bracket-lib. This macro is a thin wrapper over the built-in include_bytes! macro. The path you provide must be the path Cargo will see when it runs the build. For example, the following line of code embeds flappy32.png from the FlappyBonus project:

This step includes your file in the program’s compiled output. The last step is to tell bracket-lib to include the file as a resource. At the top of your main.rs function, you need to link the resource (this step adds it to bracket-lib’s font system). Remove any .. from your path: this step uses the final path stored in the resource build. For example, to link the flappy32.png file we embedded a moment ago you would use:

Build and Bind

Now that your asset embedding is in place, build your project:

cargo build --target wasm32-unknown-unknown --release

Now create a directory named wasm-help (off of your project directory, next to src and in the same directory as Cargo.toml). You don’t have to name the directory that, but the examples in this article will assume that you did so. It’s time to create the JavaScript linking your program to the web browser. Run the following command (you may need to adjust the path to match your target directory if you are using workspaces):

wasm-bindgen target\wasm32-unknown-unknown\release\flappy_bonus.wasm --out-dir .\wasm_help --no-modules --no-typescript

If you look in your wasm-help directory, you’ll see that some files have appeared:

  • flappy_bonus.js which contains JavaScript bindings for your program.
  • flappy_bonus_bg.wasm which contains your WASM compiled code.

All that remains is to create a webpage to execute your program.

Create a Webpage

I typically copy/paste the following skeletal HTML into a file named index.html in the wasm-help directory and edit the text to match the game name:

This file loads the JavaScript file created for you and loads your WASM once the page has finished downloading. This step creates a sort of canvas on which the game can render. Upload your wasm-help directory to a web server (even a local one), and you can play your game in a browser.

Preparing the Hands-on Rust Dungeon Crawler — WASM Edition

The Dungeon Crawler from Hands-on Rust requires a few more changes: Legion needs to be put into single-threaded mode; you need to embed your tile graphics; and the later chapters that use data-driven definitions need to load the data from embedded resources.

We’ll work on the loot_tables example to get the finished game onto the web.

Make Legion Single-Threaded

Open your Cargo.toml file and find the dependency for Legion. Replace it with the following:

legion = { version = "=0.3.1", default-features = false, features = ["wasm-bindgen", "codegen"] }

This code disables Legion’s multi-threading and enables compatibility with wasm-bindgen.

Embed Tile Graphics

Just like you did with Flappy, you need to embed the graphics files. In main.rs (above fn main()) include the following:

embedded_resource!(TILE_FONT, "../resources/dungeonfont.png");

At the top of fn main() include:

link_resource!(TILE_FONT, "resources/dungeonfont.png");

Otherwise, your main function is the same:

Notice how the function is using terminal8x8but you don’t link it? The image is embedded by default in bracket-lib.

Load Data Files

In the Loot chapter of Hands-on Rust, you learn to use TOML files to define your data. Loading the definitions from disk offers great flexibility, but isn’t very WASM friendly — you need to embed the file in your program. Fortunately, Rust provides an include_bytes! macro to embed files in your code. You can combine this with a from_bytes function from the RON deserializer to include the file as part of your WASM build.

Open src/spawner/template.rs and replace the load() function:

The template.ron file will now be embedded in your WASM file, and the reader will load it from there.

Build the Dungeon Crawler

Next, you need to follow the same steps you used for Flappy. Create a wasm-help directory and add an index.html file to it:

Then build your WASM file:

cargo build --target wasm32-unknown-unknown --release

Now you can publish your wasm_help directory to your web server.

Here’s the Loot Tables example from Hands-on Rust, running in your browser:

0*V1OMOruK5OvTEaDq.jpg?q=20
run-your-rust-games-in-a-browser-ceea86b04616

Wrapping Up

WebAssembly is a great way to publish your games on the web. Rust has all the tools you need, and bracket-lib is designed to help you with the process. Now you can publish your game, too.

🏓 Be sure to send me a link if you publish your game to the web—I’d love to see what you come up with.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK