github.com/yuin/gopher-lua 踩坑日记
阅读原文时间:2023年09月01日阅读:1

本文主要记录下在日常开发过程中, 使用 github.com/yuin/gopher-lua 过程中需要注意的地方。

后续遇到其他的需要注意的事项再补充。

1、加载LUA_PATH环境变量

在实际开发中,我们会将一些公共的、可重复使用的代码封装起来,假如我们只是一些简单的处理,全部写在一个文件是没有问题的,维护起来也并不是很麻烦。但是当我们的需求变得复杂起来,或者需求调整的时候,我们还是将所有功能都写在同一个文件的时候,就变得不合理起来,后期的维护更是灾难性的,这个时候就需要将一些公共的、重复使用的代码,抽离出来,根据功能的不同分类,当做一个个模块,在使用的时候使用 require 导入进来,这样才合理。

既然我们需要将自定义的 module 导入进来,那么我们肯定是需要设计环境变量的。

最重要的地方就是下面这点:

// 重点就是这句, 先设置环境变量, 再创建 lua 的 虚拟机, 这样环境变量才生效!!!
setLuaLibPath()
lvm := newLuaVm(pluginPath, context.Background())

接下来再详细看演示的demo。

先看看代码目录分层,有一个整体认识。

--go_call_lua_test
|---go.mod
|---main.go
|---plugin.lua
|---lib/lua/test.lua

main.go 代码

package main

import (
    "context"
    "errors"
    "fmt"
    lua "github.com/yuin/gopher-lua"
    "go.uber.org/atomic"
    luar "layeh.com/gopher-luar"
    "os"
    "path/filepath"
)

func getExeDir() (string, string) {
    exePath, _ := os.Executable()
    exeDir, exeName := filepath.Split(exePath)
    return exeDir, exeName
}

func absPath(fp string) string {
    exeAbsDir, _ := getExeDir()
    //绝对路径
    if !filepath.IsAbs(fp) {
        return filepath.Join(exeAbsDir, fp)
    } else {
        return fp
    }
}

func setLuaLibPath() {
    //设置lua环境变量
    pathStr := ""
    LuaLibPath := []string{"./lib/lua/?.lua"}
    for _, path := range LuaLibPath {
        pathStr += ";" + absPath(path)
    }
    pathStr += ";;"
    os.Setenv("LUA_PATH", pathStr)
}

type LuaVm struct {
    *lua.LState
    path   string
    Loaded atomic.Bool
}

func newLuaVm(path string, ctx context.Context) *LuaVm {
    lv := &LuaVm{
        path:   path,
        LState: lua.NewState(),
    }
    lv.SetContext(ctx)
    if err := lv.load(); err != nil {
        panic(fmt.Errorf("lvm error=%s", err))
    }
    return lv
}

func (lv *LuaVm) load() error {
    //加载工具 下面是将 go 提供的功能当做模块 给 lua 使用
    //lv.SetGlobal("FILE", luar.New(lv.LState, &test.FileTool{}))

    if len(lv.path) == 0 {
        return fmt.Errorf("plugin file empty")
    }
    if err := lv.DoFile(lv.path); err != nil {
        return err
    }

    lv.Loaded.Store(true)
    return nil
}

func (lv *LuaVm) CallLua() (result string, err error) {

    if err := lv.CallByParam(lua.P{
        Fn:      lv.GetGlobal("PluginTest"),
        NRet:    2,
        Protect: true,
    }, luar.New(lv.LState, "I'm coming go!")); err != nil {
        fmt.Printf("PrimaryIn error=%s", err)
    }
    retCode, _ := lv.Get(-1).(lua.LNumber)
    lv.Pop(1)

    retPkt, _ := lv.Get(-1).(lua.LString)
    lv.Pop(1)

    if retCode != 0 {
        return "", errors.New("untreated")
    }

    return string(retPkt), nil
}

func main() {
    pluginPath := "./plugin.lua"

    // 重点就是这句, 先设置环境变量, 再创建 lua 的 虚拟机, 这样环境变量才生效!!!
    setLuaLibPath()
    lvm := newLuaVm(pluginPath, context.Background())

    ret, err := lvm.CallLua()
    fmt.Println("ret --> ", ret)
    fmt.Println("err --> ", err)
}

plugin.lua 代码

local TestLib = require("test")

function PluginTest()
    print("coming PluginTest")
    TestLib.CallTestFunc()

    return "I'm coming lua!"
end

test.lua 代码

justTest = {}

function justTest.CallTestFunc()
    print("coming justTest.CallTestFunc")
end

-- 一定要 return, 否则不生效
return justTest

编译后执行结果:

coming PluginTest
coming justTest.CallTestFunc
ret -->  I'm coming lua!
err -->  <nil>

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章