Search code examples
c++luarpclua-userdata

Lua RPC and userdata


I'm currently using luarpc in my program to make interprocess communication. The problem now is that due to my tolua++ binding which stores class instances as userdata im unable to use any of those functions cause luarpc cant handle userdata. My question now is if would be possible (and how) to transmit userdata if you know that its always only a pointer (4 Bytes) and has a metatable attached for call and indexing operations.


Solution

  • Ok i got it working now. What i did is for userdata args/returns i send the actual ptr + metatable name(typename) to the client. the client then attaches a metatable with an __index method that creates a new helper with the typename and appends a helper with the field you want to access. when you then call or read a field from that userdata the client sends the data for calling a field of the typetable and the userdata to the server.

    ReadVariable:

        lua_pushlightuserdata(L,msg.read<void*>());
    #ifndef RPC_SERVER
        luaL_getmetatable(L,"rpc.userdata");
        int len = msg.read<int>();
        char* s = new char[len];
        msg.read((uint8*)s,len);
        s[len] = '\0';
        lua_pushlstring(L,s,len);
        lua_setfield(L,-2,"__name");
        lua_pushlightuserdata(L,TlsGetValue(transporttls));
        lua_setfield(L,-2,"__transport");
        lua_setmetatable(L,-2);
    #endif
    

    Write Variable:

        else
        {
            msg.append<RPCType>(RPC_USERDATA);
            msg.append<void*>(lua_touserdata(L,idx));
    #ifdef RPC_SERVER
            lua_getmetatable(L,idx);
            lua_rawget(L,LUA_REGISTRYINDEX);
            const char* s = lua_tostring(L,-1);
            int len = lua_strlen(L,-1);
            msg.append<int>(len);
            msg.append(s,len);
    #endif
            lua_settop(L,stack_at_start);
        }
    

    userdata indexing:

    checkNumArgs(L,2);
    ASSERT(lua_isuserdata(L,1) && isMetatableType(L,1,"rpc.userdata"));
    
    if(lua_type(L,2) != LUA_TSTRING)
        return luaL_error( L, "can't index a handle with a non-string" );
    const char* s = lua_tostring(L,2);
    if(strlen(s) > MAX_PATH - 1)
        return luaL_error(L,"string to long");
    
    int stack = lua_gettop(L);
    lua_getmetatable(L,1);
    lua_getfield(L,-1,"__name");
    const char* name = lua_tostring(L,-1);
    if(strlen(name) > MAX_PATH - 1)
        return luaL_error(L,"string to long");
    lua_pop(L,1); // remove name
    
    lua_getfield(L,-1,"__transport");
    Transport* t = reinterpret_cast<Transport*>(lua_touserdata(L,-1));
    lua_pop(L,1);
    
    Helper* h  = Helper::create(L,t,name);
    Helper::append(L,h,s);
    return 1;
    

    well i more or less rewrote the complete rpc library to work with named pipes and windows but i think the code should give anyone enough information to implement it.

    this allows code like:

    local remote = rpc.remoteobj:getinstance()
    remote:dosmthn()
    

    on the clientside. it currently doesnt allow to add new fields but well this is all i need for now :D