1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        保姆級教程: c++游戲服務(wù)器嵌入v8 js引擎

        共 7001字,需瀏覽 15分鐘

         ·

        2021-08-19 08:24


        導(dǎo)語 | 本文將介紹在c++游戲服務(wù)器上嵌入v8 js引擎的詳細(xì)教程,關(guān)鍵步驟都會附帶完整的可運(yùn)行代碼。并在文末為您附上github倉庫鏈接。


        逐漸有些原生語言項目因為希望有不停機(jī)更新的能力而引入腳本。而且由于大多數(shù)項目已經(jīng)有現(xiàn)成的c++服務(wù)器框架,他們往往選擇把腳本作為庫嵌入到c++程序的做法。


        服務(wù)器選用一個庫,最看重的莫過于穩(wěn)定性和性能了,在眾多腳本引擎中,v8這兩方面可謂佼佼者:穩(wěn)定性源自長時間各種方式的折騰,v8引擎每天那么多的實例跑在各種各樣的機(jī)器、環(huán)境下,跑著各種各樣的代碼,一天跑的代碼量比很多小眾的腳本引擎一輩子的代碼量還多,而且nodejs的應(yīng)用也驗證了v8跑在服務(wù)器環(huán)境是沒問題的。


        性能這塊,在jit的加持下,雖說比不上原生語言,但在腳本中肯定是第一檔的存在。


        對于c++程序猿,v8還有個很誘人的地方:支持wasm,c++編譯成wasm在v8上跑,性能比js還能高一個臺階,而且還能熱更新。


        v8引擎看上去很合適服務(wù)器使用,目前卻很少項目應(yīng)用到游戲服務(wù)器上,一些項目交流說有過這樣的想法,但不知道怎么做v8嵌入。于是有了本文,本文會循序漸進(jìn)的介紹怎么在linux c++程序里頭嵌入v8:


        • HelloWorld級別的示例;

        • c++類封裝到j(luò)s;

        • 把v8改為嵌入式nodejs;


        上述三步都會附帶完整的可運(yùn)行代碼,最后會附上github倉庫鏈接。



        一、HelloWorld


        直接上王道:


        //...各種include

        // -------------------------begin 1-----------------------------
        static void Print(const v8::FunctionCallbackInfo<v8::Value>& info) {
        v8::Isolate* isolate = info.GetIsolate();
        v8::Local<v8::Context> context = isolate->GetCurrentContext();

        std::string msg = *(v8::String::Utf8Value(isolate, info[0]->ToString(context).ToLocalChecked()));
        std::cout << msg << std::endl;
        }
        // -------------------------end 1-----------------------------
        int main(int argc, char* argv[]) {
        // -------------------------begin 2-----------------------------
        // Initialize V8.
        v8::StartupData SnapshotBlob;
        SnapshotBlob.data = (const char *)SnapshotBlobCode;
        SnapshotBlob.raw_size = sizeof(SnapshotBlobCode);
        v8::V8::SetSnapshotDataBlob(&SnapshotBlob);

        std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
        v8::V8::InitializePlatform(platform.get());
        v8::V8::Initialize();

        // Create a new Isolate and make it the current one.
        v8::Isolate::CreateParams create_params;
        create_params.array_buffer_allocator =
        v8::ArrayBuffer::Allocator::NewDefaultAllocator();
        v8::Isolate* isolate = v8::Isolate::New(create_params);
        // -------------------------end 2-----------------------------
        {
        // -------------------------begin 3-----------------------------
        v8::Isolate::Scope isolate_scope(isolate);

        // Create a stack-allocated handle scope.
        v8::HandleScope handle_scope(isolate);

        // Create a new context.
        v8::Local<v8::Context> context = v8::Context::New(isolate);

        // Enter the context for compiling and running the hello world script.
        v8::Context::Scope context_scope(context);

        // -------------------------end 3-----------------------------
        // -------------------------begin 4-----------------------------
        context->Global()->Set(context, v8::String::NewFromUtf8(isolate, "Print").ToLocalChecked(),
        v8::FunctionTemplate::New(isolate, Print)->GetFunction(context).ToLocalChecked())
        .Check();

        // -------------------------end 4-----------------------------
        {
        // -------------------------begin 5-----------------------------
        const char* csource = R"(
        Print('hello world');
        )";

        // Create a string containing the JavaScript source code.
        v8::Local<v8::String> source =
        v8::String::NewFromUtf8(isolate, csource)
        .ToLocalChecked();

        // Compile the source code.
        v8::Local<v8::Script> script =
        v8::Script::Compile(context, source).ToLocalChecked();

        // Run the script
        auto _unused = script->Run(context);
        }
        // -------------------------end 5-----------------------------
        }
        // -------------------------begin 6-----------------------------
        // Dispose the isolate and tear down V8.
        isolate->Dispose();
        v8::V8::Dispose();
        v8::V8::ShutdownPlatform();
        delete create_params.array_buffer_allocator;
        return 0;
        // -------------------------end 6-----------------------------
        }

         

        以上一大堆代碼最終運(yùn)行效果只是打印了個“hello world”,沒接觸過的童靴是不是有點暈菜,別急,有我。


        上述代碼我用分割線分成了6塊,其中:


        • 第2塊是v8的啟動,第6塊是v8的關(guān)閉,除非你要定制啟動參數(shù),啟動多虛擬機(jī)啥的,否則這兩部分都是固定的;


        • 第1塊有個Print函數(shù),和這函數(shù)同聲明的c++函數(shù),都可以注冊到j(luò)s環(huán)境里頭被js調(diào)用,函數(shù)只是簡單的把參數(shù)取出通過std::cout輸出;


        • 第4塊把前面的Print函數(shù)注冊到j(luò)s的全局變量,名字也叫Print;


        • 第5塊執(zhí)行了一段js代碼,調(diào)用了Print函數(shù)。


        上述例子演示了怎么去啟動一個腳本,以及怎么從腳本調(diào)用原生。在Print只是簡單的取一個參數(shù)進(jìn)行打印,如果有更多個數(shù)及種類的參數(shù)呢?更復(fù)雜的是一個c++類有構(gòu)造函數(shù),成員變量,有成員函數(shù),靜態(tài)函數(shù),還有繼承,重載等等,c++類如果需要封裝不是十分麻煩?


        這就輪到puerts出場了,為服務(wù)器童鞋科普下:puerts最初是Unreal Engine、Unity游戲引擎下的typescript編程解決方案,但游戲引擎以外的環(huán)境也逐步在支持,其中任意C#環(huán)境早已支持,而c++ 11以上環(huán)境,最近也加入支持之列。通過puerts,我們僅僅只需對c++進(jìn)行些聲明操作,即可被js使用,甚至可以根據(jù)c++ api生成.d.ts文件。


        二、
        Powered by Puerts


        用個比較簡單又有一定代表性的c++類為例:


        class TestClass
        {
        public:
        TestClass(int p) {
        std::cout << "TestClass(" << p << ")" << std::endl;
        X = p;
        }

        static void Print(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;
        };


        對上述類,只需要在c++里頭做如下聲明:


        UsingCppType(TestClass);

        int main(int argc, char* argv[]) {
        //other...

        //注冊
        puerts::DefineClass<TestClass>()
        .Constructor<int>()
        .Function("Print", MakeFunction(&TestClass::Print))
        .Property("X", MakeProperty(&TestClass::X))
        .Method("Add", MakeFunction(&TestClass::Add))
        .Register();
        //other...
        }


        然后就能在js里頭使用(ps,puerts還支持對上述類生成typescript類型定義):


        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));


        當(dāng)然,要支持這些,還需要對puerts做一定的初始化操作,在這就不再贅述,各位可于文后鏈接獲取代碼,對比第一版Helloworld即可得知用法。


        至此,我們能在c++程序里執(zhí)行js代碼, js能調(diào)用到c++代碼。這對一些項目已經(jīng)足夠了。


        不過我們嵌入的v8引擎,只實現(xiàn)了es規(guī)范語法以及api,像setTimeout這種耳熟能詳?shù)腶pi,都不是es規(guī)范的內(nèi)容,其次有的項目組希望能對接npm上豐富的組件,那有沒可能往c++程序嵌入一個nodejs呢?請看下一章節(jié)。


        三、Powered by embedding Nodejs


        第一步我們要編譯libnode.so,下載或者clone node源碼,進(jìn)入源碼目錄,執(zhí)行如下命令:


        ./configure --shared
        make -j4


        漫長的編譯完成后,會在out/Release/下找到libnode.so.95文件,這就是動態(tài)庫版本的node,接下來編譯官方的嵌入式例子:


        cd test/embedding
        c++ -I../../src -I../../deps/v8/include -I../../deps/uv/include embedtest.cc -c embedtest.cc
        c++ embedtest.o -Wl,-rpath,../../out/Release ../../out/Release/libnode.so.95


        跑一下:


        ./a.out "console.log('hello world')"


        跟著,我們把上一章節(jié)的TestClass,Puerts加入到這程序,然后在js里試試看?


        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);


        除了之前的c++類調(diào)用之外,還加了nodejs api的調(diào)用,以證明這確實是個完整的nodejs環(huán)境。


        nodejs的嵌入可能要了解的情況更多,它內(nèi)部有一套事件循環(huán)處理邏輯,也會啟動些線程,要注意這些是否和原來的服務(wù)器框架有沖突。相比之下,上一章節(jié)的純v8環(huán)境只是一個庫,它跑不跑取決于你是否調(diào)用,會簡單得多。


        附上完整的實例代碼以及編譯配置,按readme操作就可以運(yùn)行:

        https://github.com/chexiongsheng/v8_embedding_test。



         作者簡介


        車雄生(johnche)

        騰訊游戲開發(fā)工程師

        騰訊游戲開發(fā)工程師,從事游戲開發(fā)工作多年,目前于騰訊游戲中臺部門負(fù)責(zé)公共組件開發(fā),是三個騰訊開源組件:xLua、InjectFix、Puerts的作者。


        推薦閱讀


        程序員如何把你關(guān)注的內(nèi)容推送到你眼前?揭秘信息流推薦背后的系統(tǒng)設(shè)計

        在Exception的影響下,如何才能寫出更高質(zhì)量的C++代碼?

        自動的內(nèi)存管理系統(tǒng)實操手冊——Golang垃圾回收篇

        自動的內(nèi)存管理系統(tǒng)實操手冊——Java和Golang對比篇

        自動的內(nèi)存管理系統(tǒng)實操手冊——Java垃圾回收篇



        瀏覽 107
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            特级西西444www精品视频 | 色色网站入口 | 亚洲啪啪啪| 免费无码成人片在线观看不卡 | 桥本爱实大尺度三级 | 男女互舔下面视频 | 高清无码一级片 | 少妇宾馆露脸高潮不断 | 太舒服了,快点,受不了了 | 天天综合网~永久入口红桃 |