Step‑by‑Step Guide: Embedding V8 and Node.js into a C++ Game Server
This article walks through embedding the V8 JavaScript engine into a C++ game server, demonstrates a HelloWorld example, shows how to bind C++ classes using Puerts, and explains how to compile and embed a full Node.js runtime, with complete code snippets and a GitHub repository link.
HelloWorld Example
This minimal example shows how to embed the V8 JavaScript engine into a Linux C++ program. The implementation is split into six logical blocks:
Block 1 : Define a Print callback that extracts the first argument from the V8 FunctionCallbackInfo and writes it to std::cout.
Block 2 : Initialise V8, create a snapshot blob, set up the default platform and initialise the V8 engine.
Block 3 : Create an Isolate, an Isolate::Scope, a HandleScope and a new execution Context.
Block 4 : Register the Print function as a global JavaScript function in the context.
Block 5 : Compile a short script that calls Print('hello world') and execute it.
Block 6 : Dispose the isolate, shut down the platform and clean up allocated resources.
The complete source code (without styling attributes) can be compiled with any standard C++ tool‑chain:
static void Print(const v8::FunctionCallbackInfo<v8::Value>& info) {
v8::Isolate* isolate = info.GetIsolate();
v8::String::Utf8Value utf8(isolate, info[0]);
std::cout << *utf8 << std::endl;
}
int main(int argc, char* argv[]) {
// Initialise V8
v8::V8::InitializeICUDefaultLocation(argv[0]);
v8::V8::InitializeExternalStartupData(argv[0]);
std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
v8::V8::InitializePlatform(platform.get());
v8::V8::Initialize();
// Create a new Isolate
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
v8::Isolate* isolate = v8::Isolate::New(create_params);
{
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
// Register Print
context->Global()->Set(context,
v8::String::NewFromUtf8(isolate, "Print").ToLocalChecked(),
v8::FunctionTemplate::New(isolate, Print)->GetFunction(context).ToLocalChecked()
).Check();
// Compile and run script
const char* source = "Print('hello world');";
v8::Local<v8::String> src = v8::String::NewFromUtf8(isolate, source).ToLocalChecked();
v8::Local<v8::Script> script = v8::Script::Compile(context, src).ToLocalChecked();
script->Run(context);
}
// Cleanup
isolate->Dispose();
v8::V8::Dispose();
v8::V8::ShutdownPlatform();
delete create_params.array_buffer_allocator;
return 0;
}Binding C++ Classes with Puerts
Puerts provides a lightweight bridge that lets C++ classes be exposed to JavaScript (or TypeScript) without writing manual glue code. The following TestClass demonstrates constructor, static method, instance method, and property binding:
class TestClass {
public:
TestClass(int p) { std::cout << "TestClass(" << p << ")" << std::endl; X = p; }
static void Print(const std::string& msg) { std::cout << msg << std::endl; }
int Add(int a, int b) { std::cout << "Add(" << a << "," << b << ")" << std::endl; return a + b; }
int X;
};
// Register the class with Puerts
usingCppType(TestClass);
puerts::DefineClass<TestClass>()
.Constructor<int>()
.Function("Print", MakeFunction(&TestClass::Print))
.Method("Add", MakeFunction(&TestClass::Add))
.Property("X", MakeProperty(&TestClass::X))
.Register();In JavaScript the class can be loaded and used as follows:
const TestClass = loadCppType('TestClass');
TestClass.Print('hello world');
let obj = new TestClass(123);
TestClass.Print(obj.X);
obj.X = 99;
TestClass.Print(obj.X);
TestClass.Print('ret = ' + obj.Add(1, 3));Embedding a Full Node.js Runtime
When the full Node.js ecosystem (e.g., setTimeout, npm packages) is required, the guide shows how to build a shared library libnode.so from the Node.js source and link it with a small embedding program that also includes V8, libuv and Node headers.
Build libnode.so
./configure --shared
make -j4
# The resulting library is out/Release/libnode.so.95Compile the embedding test program (example command, paths may need adjustment):
c++ -I./src -I./deps/v8/include -I./deps/uv/include \
embedtest.cc -c -o embedtest.o
c++ embedtest.o -Wl,-rpath,./out/Release \
-L./out/Release -lnode.so.95 -o embedtestRunning the program with a simple script demonstrates that both the custom C++ class (registered via Puerts) and native Node.js APIs are available:
.\/embedtest "console.log('hello world')"Example JavaScript used in the embedded Node.js environment:
const TestClass = loadCppType('TestClass');
TestClass.Print('hello world');
let obj = new TestClass(123);
TestClass.Print(obj.X);
obj.X = 99;
TestClass.Print(obj.X);
TestClass.Print('ret = ' + obj.Add(1, 3));
const fs = require('fs');
let info = fs.readdirSync('.');
console.log(info);Embedding Node.js introduces an event loop and additional threads; developers should verify that this does not conflict with the existing server framework.
All source code, build scripts and a detailed README are available in the GitHub repository:
https://github.com/chexiongsheng/v8_embedding_test
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Tencent Cloud Developer
Official Tencent Cloud community account that brings together developers, shares practical tech insights, and fosters an influential tech exchange community.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
