
1. Introduction
Since being captivated by the MOD magic of Warcraft III in my childhood, I have always had a special affection for game scripting languages.
Looking back, developing game levels for Warcraft III using Blizzard’s JASS language, although JASS seems extremely rudimentary from today’s perspective, characterized by static typing and no garbage collection, it represented a bold attempt at game development languages during an era when industry standards had not yet formed.
Why Use Scripting Languages for Game Development?
The introduction of game scripting languages mainly aims to improve the convenience of development and testing. If a low-level language such as C++ is used directly, modifying a single line of code may require significant time waiting for the compilation and packaging of a complex toolchain. However, by using scripting languages, the programs that implement game mechanics can be executed with hot reloading, significantly enhancing development efficiency.
Over time, dynamic typing scripting languages like Lua and JavaScript have become common in game development.
However, with the evolution of programming languages, we have the opportunity to redefine a new standard for game scripting languages—both retro and innovative, which is the combination of Rust and WASM.
2. Rust + WASM + Dora SSR: Redefining Game Script Development
By combining Rust and WASM, we can perform game hot updates and testing directly on devices such as Android or iOS without sacrificing performance and without relying on traditional application development toolchains.
Moreover, with the Web IDE interface of the open-source Dora SSR game engine, game code written in Rust can be tested and run on various gaming devices after a single compilation.
Why Choose Rust?
Rust provides unparalleled memory safety guarantees and does not require the intervention of a garbage collector (GC), making it highly suitable for game development, especially in performance-sensitive scenarios. Combined with WASM, Rust can provide high-performance execution efficiency while maintaining cross-platform consistency and safety.
Quick Start Guide
Before we start development, we need to install the Dora SSR game engine. This engine supports multiple platforms, including Windows, Linux, macOS, iOS, and Android.
For specific installation steps and requirements, please refer to the official quick start guide: Dora SSR Quick Starthttps://dora-ssr.net/zh-Hans/docs/tutorial/quick-start/.
Step 1: Create a New Project
Step 2: Write Game Code
Then create a new Rust project in the command line:
rustup target add wasm32-wasi
cargo new hello-dora --name init
cd hello-dora
cargo add dora_ssr
src/main.rs
:use dora_ssr::*;
fn main () {
let mut sprite = match Sprite::with_file("Image/logo.png") {
Some(sprite) => sprite,
None => return,
};
let mut sprite_clone = sprite.clone();
sprite.schedule(once(move |mut co| async move {
for i in (1..=3).rev() {
p!("{}", i);
sleep!(co, 1.0);
}
p!("Hello World");
sprite_clone.perform_def(ActionDef::sequence(&vec![
ActionDef::scale(0.1, 1.0, 0.5, EaseType::Linear),
ActionDef::scale(0.5, 0.5, 1.0, EaseType::OutBack),
]));
}));
}
cargo build --release --target wasm32-wasi
Step 3: Upload and Run the Game
init.wasm
.
python3 upload.py "192.168.3.1" "Hello"

Step 4: Publish the Game
In the editor’s left game resource tree, right-click the newly created project folder and select ‘Download’.
Wait for the browser to pop up a download prompt for the packaged project files.
3. How It Was Achieved
Implementing Rust language development support and embedding the WASM runtime in Dora SSR is a new technical exploration and attempt, mainly involving three key steps:
1. Designing the Interface Definition Language (IDL)
To embed the WASM runtime on a game engine written in C++ and support the Rust language, it is first necessary to design an interface definition language (IDL) to facilitate communication and data exchange between different programming languages.
Below is an example of a WASM IDL designed by Dora SSR, based on the program interface of the source language C++, with additional tags required for conversion to the Rust interface, such as object, readonly, optional, etc.
A challenge in cross-language interface mapping is that C++ interface design is object-oriented, while Rust does not provide complete object-oriented design capabilities, so some object-oriented interfaces need to be simulated in Rust with additional code. Fortunately, the language differences are not particularly large, and they can be resolved without complex mechanism designs.
object class EntityGroup @ Group
{
readonly common int count;
optional readonly common Entity* first;
optional Entity* find(function<bool(Entity* e)> func) const;
static EntityGroup* create(VecStr components);
};
2. Writing the Program to Generate Glue Code
The second step is to write a program that generates glue code for mutual invocation between C++, WASM, and Rust through the IDL.
To achieve this, we chose to use Yuescript, a language created by the Dora SSR project. Yuescript is a dynamic programming language based on Lua, combined with the lpeg syntax parsing library from the Lua language ecosystem to handle IDL parsing and glue code generation.
The benefit of using Yuescript is that it inherits the flexibility and lightweight nature of Lua while providing richer syntax and functionalities, suitable for handling complex code generation tasks.
Below is an excerpt of code for an IDL parser written using PEG grammar.
Param = P {
"Param"
Param: V"Func" * White * Name / mark"callback" + Type * White * Name / mark"variable"
Func: Ct P"function<" * White * Type * White * Ct P"(" * White * (V"Param" * (White * P"," * White * V"Param")^0 * White)^-1 * P")" * White * P">"
}
Method = Docs * Ct(White * MethodLabel) * White * Type * White * (C(P"operator==") + Name) * White * (P"@" * White * Name + Cc false) * White * Ct(P"(" * White * (Param * (White * P"," * White * Param)^0 * White)^-1 * P")") * White * C(P"const")^-1 * White * P";" / mark"method"
3. Embedding the WASM Runtime and Code Integration
The final step is embedding the WASM runtime and the generated C++ glue code into the game engine, completing the code integration. For the WASM runtime, we chose to use WASM3, a high-performance, lightweight WebAssembly interpreter that supports multiple CPU architectures, simplifying the complexity of the compilation chain and enhancing cross-platform compatibility. Through this method, Dora SSR can support running games developed in Rust on various hardware devices, greatly improving the accessibility and flexibility of game projects.
During the integration process, we released a crate package for Rust developers, containing all necessary interfaces and tools, allowing developers to easily develop and republish other game modules written in Rust based on the Dora SSR game engine in the future.
4. Performance Comparison
The Dora SSR game engine also provides support for the Lua scripting language. Currently, it uses version 5.5 of the Lua virtual machine, which, like WASM3, does not perform JIT real-time machine code generation but interprets script code in the virtual machine. Therefore, we can make some performance comparisons between these two similar scripting solutions.
Before the comparison, we can roughly judge that, not considering the time consumed by Lua’s garbage collection, due to the dynamic nature of the Lua language, the program interfaces mapped from C++ to Lua often require runtime checks of parameter types during invocation, and the lookup of member attributes of Lua objects also needs to be done at runtime through a hash structure table. These are overheads that the statically typed Rust language + WASM virtual machine do not need to incur, or only incur in smaller overhead scenarios.
Below are some basic performance test cases, specifically selecting interfaces from the C++ side that do not perform much computation to compare the performance differences of cross-language invocation and parameter passing.
-
Rust Test Code
let mut root = Node::new();
let node = Node::new();
let start = App::get_elapsed_time();
for _ in 0..10000 {
root.set_transform_target(&node);
}
p!("object passing time: {} ms", (App::get_elapsed_time() - start) * 1000.0);
let start = App::get_elapsed_time();
for _ in 0..10000 {
root.set_x(0.0);
}
p!("number passing time: {} ms", (App::get_elapsed_time() - start) * 1000.0);
let start = App::get_elapsed_time();
for _ in 0..10000 {
root.set_tag("Tag name");
}
p!("string passing time: {} ms", (App::get_elapsed_time() - start) * 1000.0);
-
Lua Test Code
local root = Node()
local node = Node()
local start = App.elapsedTime
for i = 1, 10000 do
root.transformTarget = node
end
print("object passing time: " .. tostring((App.elapsedTime - start) * 1000) .. " ms")
start = App.elapsedTime
for i = 1, 10000 do
root.x = 0
end
print("number passing time: " .. tostring((App.elapsedTime - start) * 1000) .. " ms")
start = App.elapsedTime
for i = 1, 10000 do
root.tag = "Tag name"
end
print("string passing time: " .. tostring((App.elapsedTime - start) * 1000) .. " ms")
Running Results
Rust + WASM:
object passing time: 0.6279945373535156 ms
number passing time: 0.5879402160644531 ms
string passing time: 3.543853759765625 ms
Lua:
object passing time: 6.7338943481445 ms
number passing time: 2.687931060791 ms
string passing time: 4.2259693145752 ms
It can be seen that, except for the string type interface parameter passing, the performance of other types of interfaces implemented in Dora SSR for Lua cross-language invocation is almost an order of magnitude slower than WASM cross-language invocation.
The inference for the string type interface is that the major performance consumption mainly comes from the copying of string objects, and the overhead of cross-language invocation is much smaller than the overhead of memory copying, so the result difference is not significant.
5. User Experience Insights
Introducing the Rust language into game development, I personally experienced a productivity boost that differs from traditional methods, especially in assisting code generation with large language models (like ChatGPT). Compared to traditional C or C++, Rust’s strict compiler provides a more robust and secure programming environment for game development.
For instance, when using large language models to assist coding, while the generated code in C or C++ can often compile, many hidden errors and defects may still exist at runtime. These issues may include memory leaks, pointer misuse, or reference misuse, which are common and difficult-to-debug problems in game development.
However, in Rust, many such problems can be effectively captured and corrected at the compilation stage, thanks to Rust’s ownership and borrowing mechanisms, as well as its design advantages in type safety and memory safety.
By introducing support for Rust in the Dora SSR game engine, I found that writing game scripts is not only safer but also more efficient. This makes game development no longer a process of debugging errors but rather a more focused process of creating and realizing the imagined game.
These advantages of Rust, coupled with the cross-platform capabilities of WASM, greatly expand our game development capabilities and possibilities.
6. Conclusion
Choosing Dora SSR + Rust as a game development tool is not only a pursuit of cutting-edge technology but also a new exploration of the game development process. Here, I sincerely invite every friend who loves game development to join our community and explore this exciting technological journey together.
Our QQ group is here, welcome to join: 512620381
Author Introduction
Li Jin: Big Data Engineer in the financial industry, author of the open-source software Dora SSR and Yuescript.
Project Introduction
Dora SSR (Dora Wonder Engine) is a game engine for rapidly developing 2D games on various devices. It comes with an easy-to-use development toolchain that supports direct game development on mobile phones and open-source handheld devices.

Project Repository
https://gitee.com/pig/Dora-SSRhttps://github.com/IppClub/Dora-SSR
Popular Articles
– The World’s First “Open Source” Renowned Family
– Robin Complained About Closed Source, Zuck Directly Responded…
– Rural Chicken is “Open Source” Now
– Is the Open Source Secondary Screen “Operating System” Built on Electron a Productivity Tool or a Beautiful Waste?
– Legendary Programmer Fabrice Bellard Releases Audio Compression Tool TSAC
