知行合一

致良知


  • 首页

  • 归档

  • 站点地图

cocos2dx lua 集成 google protobuf

发表于 2019-09-20 |

选择支持google protobuf的lua库

  • 选择了几个相对质量不错的库
  1. lua-protobuf,A Lua module to work with Google protobuf
  2. pbc,A protocol buffers library for C
  3. LuaPbIntf,Binding Protobuf 3 to Lua 5.3
  4. libprotolua,A Lua module to work with Google protobuf
  • 经过对比,pbc相对名气比较大,且已有蛮多人使用于cocos2dx项目,比较成熟;问题是目测不支持proto3,在lua5.1环境下不支持int64(cocos2dx使用的是luajit2.x,对应lua5.1的版本,可以排除LuaPbIntf);整个对比不细说,总的来说pbc>lua-protobuf>libprotolua,所以这边选择pbc集成

pbc集成

  • 添加c源码:在“frameworks\cocos2d-x\external\pbc”目录下添加pbc源码(pbc/src),并在本目录下添加CMakeLists.txt(Android studio默认是使用cmake编译了)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    cmake_minimum_required(VERSION 3.6)

    set(lib_name pbc)
    set(target_name ext_${lib_name})

    project(${lib_name})

    set(${target_name}_src
    alloc.c
    array.c
    bootstrap.c
    context.c
    decode.c
    map.c
    pattern.c
    proto.c
    register.c
    rmessage.c
    stringpool.c
    varint.c
    wmessage.c
    )

    add_library(${target_name} STATIC
    ${${target_name}_src}
    )

    target_include_directories(${target_name} PUBLIC ..)

    set_target_properties(${target_name}
    PROPERTIES
    ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
    LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
    FOLDER "External"
    )
  • 修改c源码对应的cmake脚本:在“frameworks\cocos2d-x\external”目录的CMakeList.txt添加以下内容

    1
    2
    # 在add_subdirectory(md5)后添加
    add_subdirectory(pbc)
  • 添加lua绑定源码:在“frameworks\cocos2d-x\cocos\scripting\lua-bindings\manual\pbc”目录添加lua绑定代码(pbc-lua.c、pbc-lua.h、lua_cocos2dx_pbc_manual.cpp、lua_cocos2dx_pbc_manual.h)

  1. pbc-lua.c 为 “pbc/binding/lua/pbc-lua.c”

  2. pbc-lua.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// pbc-lua.h
#pragma once

#ifdef __cplusplus
extern "C" {
#endif
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

#ifdef __cplusplus
}
#endif

#ifdef __cplusplus
extern "C" {
#endif
int luaopen_protobuf_c(lua_State *L);
#ifdef __cplusplus
}
#endif
  1. lua_cocos2dx_pbc_manual.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// lua_cocos2dx_pbc_manual.cpp
#include "scripting/lua-bindings/manual/pbc/lua_cocos2dx_pbc_manual.h"

#include "platform/CCPlatformConfig.h"
#include "base/ccConfig.h"
#include "scripting/lua-bindings/manual/tolua_fix.h"
#include "scripting/lua-bindings/manual/LuaBasicConversions.h"
#include "scripting/lua-bindings/manual/CCLuaEngine.h"

#include "scripting/lua-bindings/manual/pbc/pbc-lua.h"

#include "cocos/platform/CCFileUtils.h"

int read_protobuf_file(lua_State *L){
const char *buff = luaL_checkstring(L, -1);
Data data = cocos2d::FileUtils::getInstance()->getDataFromFile(buff);
lua_pushlstring(L, (const char*)data.getBytes(), data.getSize());
return 1;
}

TOLUA_API int register_pbc_module(lua_State* L)
{
lua_getglobal(L, "_G");
if (lua_istable(L, -1))//stack:...,_G,
{
lua_register(L, "read_protobuf_file_c", read_protobuf_file);
luaopen_protobuf_c(L);
}
lua_pop(L, 1);
return 1;
}
  1. lua_cocos2dx_pbc_manual.h
1
2
3
4
5
6
7
8
9
10
11
12
# lua_cocos2dx_pbc_manual.h
#pragma once

#ifdef __cplusplus
extern "C" {
#endif
#include "tolua++.h"
#ifdef __cplusplus
}
#endif

TOLUA_API int register_pbc_module(lua_State* L);
  • 修改lua绑定源码的cmake脚本:修改“frameworks\cocos2d-x\cocos\scripting\lua-bindings”下的CMakeList.txt
1
2
# 在lua_bindings_manual_headers最后面添加manual/pbc/lua_cocos2dx_pbc_manual.h、manual/pbc/pbc-lua.h
# 在lua_bindings_manual_files最后面添加manual/pbc/lua_cocos2dx_pbc_manual.cpp、manual/pbc/pbc-lua.c
  • 添加lua文件:把“pbc/binding/lua/protobuf.lua,parser.lua”复制到“src\packages\base”目录下

  • win32和ios下编译:将对应文件加到工程中就行了

  • android编译:上面已经将CMakelist.txt修改好了,直接编译就行。如果需要使用Android.mk编译,需要修改对应的脚本

pbc测试

  • 将“pbc/test/addressbook.proto”拷贝到本地测试目录
  • 生成addressbook.pb,protoc.exe从“https://github.com/protocolbuffers/protobuf/releases”下载最新版本

    1
    protoc.exe --descriptor_set_out=./addressbook.pb addressbook.proto
  • 测试代码如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    require "packages.base.protobuf"

    --addr = io.open("../../src/test/addressbook.pb","rb")
    --buffer = addr:read "*a"
    --addr:close()

    local filePath = cc.FileUtils:getInstance():fullPathForFilename("test/addressbook.pb")
    local buffer = read_protobuf_file_c(filePath)

    print("---test pbc----------")

    print(filePath..buffer)
    protobuf.register(buffer)

    local t = protobuf.decode("google.protobuf.FileDescriptorSet", buffer)

    local proto = t.file[1]

    print(proto.name)
    print(proto.package)

    local message = proto.message_type

    for _,v in ipairs(message) do
    print(v.name)
    for _,v in ipairs(v.field) do
    print("\t".. v.name .. " ["..v.number.."] " .. v.label)
    end
    end

    local addressbook = {
    name = "Alice",
    id = 12345,
    phone = {
    { number = "1301234567" },
    { number = "87654321", type = "WORK" },
    }
    }

    local code = protobuf.encode("tutorial.Person", addressbook)

    local decode = protobuf.decode("tutorial.Person" , code)

    print(decode.name)
    print(decode.id)
    for _,v in ipairs(decode.phone) do
    print("\t"..v.number, v.type)
    end

    local phonebuf = protobuf.pack("tutorial.Person.PhoneNumber number","87654321")
    local buffer = protobuf.pack("tutorial.Person name id phone", "Alice", 123, { phonebuf })
    print(protobuf.unpack("tutorial.Person name id phone", buffer))

    print("--------------------")

pbc int64 支持

由于lua 51本身不支持int64,所以得先集成一个int64库,github上有蛮多int64库,这次选择了lua-int64,集成方式跟上面类似

  1. lua 51 不支持int64,赋值的时候使用string进行赋值,如“local id=require(“int64”).new_unsigned(“9223372036854775807”)”

修改lua-int64源码,并导出以下接口函数,修改pbc的时候需要用到着两个接口函数

1
2
3
4
5
6
7
LUALIB_API void luaL_i64pushobj(lua_State *L, int64_t n) {
obj64_pushint(L, n);
}

LUALIB_API void luaL_u64pushobj(lua_State *L, uint64_t n) {
obj64_pushunsigned(L, n);
}

修改pbc-lua.c源码

  1. 添加lua-int64头文件

    1
    #include "external/lua/lint64/int64.h"
  2. 读取pb消息修改

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    static int
    _rmessage_int64(lua_State *L) {
    struct pbc_rmessage * m = (struct pbc_rmessage *)checkuserdata(L,1);
    const char * key = luaL_checkstring(L,2);
    int index = luaL_checkinteger(L,3);
    /*uint32_t v[2];
    v[0] = pbc_rmessage_integer(m, key, index, &v[1]);

    lua_pushlstring(L,(const char *)v,sizeof(v));*/

    uint32_t hi, low;
    low = pbc_rmessage_integer(m, key, index, &hi);
    uint64_t v = (uint64_t)hi << 32 | (uint64_t)low;
    luaL_u64pushobj(L, v);

    return 1;
    }

    static int
    _rmessage_int52(lua_State *L) {
    struct pbc_rmessage * m = (struct pbc_rmessage *)checkuserdata(L,1);
    const char * key = luaL_checkstring(L,2);
    int index = luaL_checkinteger(L,3);
    uint32_t hi,low;
    low = pbc_rmessage_integer(m, key, index, &hi);
    int64_t v = (int64_t)((uint64_t)hi << 32 | (uint64_t)low);

    if (hi == 0) {
    lua_pushnumber(L, (lua_Number)v);
    }
    else {
    luaL_i64pushobj(L, v);
    }

    return 1;
    }

    static int
    _rmessage_uint52(lua_State *L) {
    struct pbc_rmessage * m = (struct pbc_rmessage *)checkuserdata(L,1);
    const char * key = luaL_checkstring(L,2);
    int index = luaL_checkinteger(L,3);
    uint32_t hi,low;
    low = pbc_rmessage_integer(m, key, index, &hi);
    uint64_t v = (uint64_t)hi << 32 | (uint64_t)low;
    if (hi == 0) {
    lua_pushnumber(L, (lua_Number)v);
    } else {
    luaL_u64pushobj(L, v);
    }

    return 1;
    }
  3. 写入pb消息修改

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80

    static int
    _wmessage_int64(lua_State *L) {
    struct pbc_wmessage * m = (struct pbc_wmessage *)checkuserdata(L,1);
    const char * key = luaL_checkstring(L,2);

    int isnum;
    uint64_t v64 = luaL_tou64x(L, 3, &isnum);
    if (isnum == 0) {
    return luaL_error(L, "Need an int64 type");
    }

    pbc_wmessage_integer(m, key, (uint32_t)v64, (uint32_t)(v64 >> 32));

    /*switch (lua_type(L,3)) {
    case LUA_TSTRING : {
    size_t len = 0;
    const char * number = lua_tolstring(L,3,&len);
    if (len !=8 ) {
    return luaL_error(L,"Need an 8 length string for int64");
    }
    const uint32_t * v = (const uint32_t *) number;
    pbc_wmessage_integer(m, key, v[0] , v[1]);
    break;
    }
    case LUA_TLIGHTUSERDATA : {
    void * v = lua_touserdata(L,3);
    uint64_t v64 = (uintptr_t)v;
    pbc_wmessage_integer(m, key, (uint32_t)v64 , (uint32_t)(v64>>32));
    break;
    }
    default :
    return luaL_error(L, "Need an int64 type");
    }*/
    return 0;
    }

    static int
    _wmessage_int52(lua_State *L) {
    struct pbc_wmessage * m = (struct pbc_wmessage *)checkuserdata(L,1);
    const char * key = luaL_checkstring(L,2);

    int isnum;
    uint64_t v64 = (uint64_t)luaL_toi64x(L, 3, &isnum);
    if (isnum == 0) {
    return luaL_error(L, "Need an int type");
    }

    pbc_wmessage_integer(m, key, (uint32_t)v64, (uint32_t)(v64 >> 32));

    /*int64_t number = (int64_t)(luaL_checknumber(L,3));
    uint32_t hi = (uint32_t)(number >> 32);
    pbc_wmessage_integer(m, key, (uint32_t)number, hi);*/

    return 0;
    }

    static int
    _wmessage_uint52(lua_State *L) {
    struct pbc_wmessage * m = (struct pbc_wmessage *)checkuserdata(L,1);
    const char * key = luaL_checkstring(L,2);

    int isnum;
    uint64_t v64 = luaL_tou64x(L, 3, &isnum);
    if (isnum == 0) {
    return luaL_error(L, "Need an uint type");
    }

    pbc_wmessage_integer(m, key, (uint32_t)v64, (uint32_t)(v64 >> 32));

    /*lua_Number v = (luaL_checknumber(L,3));
    if (v < 0) {
    return luaL_error(L, "negative number : %f passed to unsigned field",v);
    }
    uint64_t number = (uint64_t)v;
    uint32_t hi = (uint32_t)(number >> 32);
    pbc_wmessage_integer(m, key, (uint32_t)number, hi);*/

    return 0;
    }
  4. 其他修改

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    static void *
    _push_value(lua_State *L, char * ptr, char type) {
    switch(type) {
    case 'u': {
    uint64_t v = *(uint64_t*)ptr;
    ptr += 8;
    //lua_pushnumber(L,(lua_Number)v);
    luaL_u64pushobj(L, v);
    break;
    }
    case 'i': {
    int32_t v = *(int32_t*)ptr;
    ptr += 4;
    lua_pushinteger(L,v);
    break;
    }
    case 'b': {
    int32_t v = *(int32_t*)ptr;
    ptr += 4;
    lua_pushboolean(L,v);
    break;
    }
    case 'p': {
    uint32_t v = *(uint32_t*)ptr;
    ptr += 4;
    lua_pushlightuserdata(L,(void *)(intptr_t)v);
    break;
    }
    case 'x': {
    /*lua_pushlstring(L,ptr,8);
    ptr += 8;*/
    uint64_t v = *(uint64_t*)ptr;
    ptr += 8;
    luaL_u64pushobj(L, v);
    break;
    }
    case 'd': {
    int64_t v = *(int64_t*)ptr;
    ptr += 8;
    //lua_pushnumber(L,(lua_Number)v);
    luaL_i64pushobj(L, v);
    break;
    }
    case 'r': {
    double v = *(double *)ptr;
    ptr += 8;
    lua_pushnumber(L,v);
    break;
    }
    case 's': {
    struct pbc_slice * slice = (struct pbc_slice *)ptr;
    lua_pushlstring(L,(const char *)slice->buffer, slice->len);
    ptr += sizeof(struct pbc_slice);
    break;
    }
    case 'm': {
    struct pbc_slice * slice = (struct pbc_slice *)ptr;
    lua_createtable(L,2,0);
    lua_pushlightuserdata(L, slice->buffer);
    lua_rawseti(L,-2,1);
    lua_pushinteger(L,slice->len);
    lua_rawseti(L,-2,2);
    ptr += sizeof(struct pbc_slice);
    break;
    }
    }
    return ptr;
    }

    static void
    _push_array(lua_State *L, pbc_array array, char type, int index) {
    switch (type) {
    case 'I': {
    int v = pbc_array_integer(array, index, NULL);
    lua_pushinteger(L, v);
    break;
    }
    case 'U': {
    uint32_t hi = 0;
    uint32_t low = pbc_array_integer(array, index, &hi);
    uint64_t v = (uint64_t)hi << 32 | (uint64_t)low;
    //lua_pushnumber(L, (lua_Number)v);
    luaL_u64pushobj(L, v);
    break;
    }
    case 'D': {
    uint32_t hi = 0;
    uint32_t low = pbc_array_integer(array, index, &hi);
    uint64_t v = (uint64_t)hi << 32 | (uint64_t)low;
    //lua_pushnumber(L, (lua_Number)((int64_t)v));
    luaL_i64pushobj(L, v);
    break;
    }
    case 'B': {
    int v = pbc_array_integer(array, index, NULL);
    lua_pushboolean(L, v);
    break;
    }
    case 'P': {
    uint32_t v = pbc_array_integer(array, index, NULL);
    lua_pushlightuserdata(L,(void *)(intptr_t)v);
    break;
    }
    case 'X': {
    uint32_t hi = 0;
    uint32_t low = pbc_array_integer(array, index, &hi);
    uint64_t v = (uint64_t)low | (uint64_t)hi << 32;
    //lua_pushlstring(L, (char *)&v, 8);
    luaL_u64pushobj(L, v);
    break;
    }
    case 'R': {
    double v = pbc_array_real(array, index);
    lua_pushnumber(L, v);
    break;
    }
    case 'S': {
    struct pbc_slice * slice = pbc_array_slice(array, index);
    lua_pushlstring(L, (const char *)slice->buffer,slice->len);
    break;
    }
    case 'M': {
    struct pbc_slice * slice = pbc_array_slice(array, index);
    lua_createtable(L,2,0);
    lua_pushlightuserdata(L,slice->buffer);
    lua_rawseti(L,-2,1);
    lua_pushinteger(L,slice->len);
    lua_rawseti(L,-2,2);
    break;
    }
    }
    lua_rawseti(L,-2,index+1);
    }


    static char *
    _get_value(lua_State *L, int index, char * ptr, char type) {
    switch(type) {
    case 'i': {
    int32_t v = luaL_checkinteger(L, index);
    memcpy(ptr, &v, 4);
    return ptr + 4;
    }
    case 'u': {
    //uint64_t v = (uint64_t)luaL_checknumber(L, index);
    uint64_t v = luaL_tou64x(L, index, NULL);
    memcpy(ptr, &v, 8);
    return ptr + 8;
    }
    case 'd': {
    //int64_t v = (int64_t)luaL_checknumber(L, index);
    int64_t v = luaL_toi64x(L, index, NULL);
    memcpy(ptr, &v, 8);
    return ptr + 8;
    }
    case 'b': {
    int32_t v = lua_toboolean(L, index);
    memcpy(ptr, &v, 4);
    return ptr + 4;
    }
    case 'p': {
    void *p = lua_touserdata(L, index);
    uint32_t v = (uint32_t)(intptr_t)p;
    memcpy(ptr, &v , 4);
    return ptr + 4;
    }
    case 'x': {
    //const char * i64 = luaL_checkstring(L, index);
    uint64_t v = luaL_tou64x(L, index, NULL);
    memcpy(ptr, &v, 8);
    return ptr + 8;
    }
    case 'r': {
    double v = luaL_checknumber(L, index);
    memcpy(ptr, &v, 8);
    return ptr + 8;
    }
    case 's': {
    size_t sz = 0;
    const char * str = luaL_checklstring(L, index, &sz);
    struct pbc_slice * slice = (struct pbc_slice *)ptr;
    slice->buffer = (void*)str;
    slice->len = sz;
    return ptr + sizeof(struct pbc_slice);
    }
    case 'm': {
    struct pbc_slice * slice = (struct pbc_slice *)ptr;
    if (lua_istable(L,index)) {
    lua_rawgeti(L,index,1);
    slice->buffer = lua_touserdata(L,-1);
    lua_rawgeti(L,index,2);
    slice->len = lua_tointeger(L,-1);
    lua_pop(L,2);
    } else {
    size_t sz = 0;
    const char * buffer = luaL_checklstring(L, index, &sz);
    slice->buffer = (void *)buffer;
    slice->len = sz;
    }
    return ptr + sizeof(struct pbc_slice);
    }
    default:
    luaL_error(L,"unknown format %c", type);
    return ptr;
    }
    }

    static void
    _get_array_value(lua_State *L, pbc_array array, char type) {
    switch(type) {
    case 'I': {
    int32_t v = luaL_checkinteger(L, -1);
    uint32_t hi = 0;
    if (v<0) {
    hi = ~0;
    }
    pbc_array_push_integer(array, v, hi);
    break;
    }
    case 'U' : {
    //uint64_t v = (uint64_t)luaL_checknumber(L, -1);
    uint64_t v = luaL_tou64x(L, -1, NULL);
    pbc_array_push_integer(array, (uint32_t)v, (uint32_t)(v >> 32));
    break;
    }
    case 'D' : {
    //int64_t v = (int64_t)luaL_checknumber(L, -1);
    int64_t v = luaL_toi64x(L, -1, NULL);
    pbc_array_push_integer(array, (uint32_t)v, (uint32_t)(v >> 32));
    break;
    }
    case 'B': {
    int32_t v = lua_toboolean(L, -1);
    pbc_array_push_integer(array, v ? 1: 0, 0);
    break;
    }
    case 'P': {
    void *p = lua_touserdata(L, -1);
    uint32_t v = (uint32_t)(intptr_t)p;
    pbc_array_push_integer(array, v, 0);
    break;
    }
    case 'X': {
    //const char * i64 = luaL_checkstring(L, -1);
    //uint64_t v = *(uint64_t *)i64;
    uint64_t v = luaL_tou64x(L, -1, NULL);
    pbc_array_push_integer(array, (uint32_t)v, (uint32_t)(v >> 32));
    break;
    }
    case 'R': {
    double v = luaL_checknumber(L, -1);
    pbc_array_push_real(array, v);
    break;
    }
    case 'S': {
    size_t sz = 0;
    const char * str = luaL_checklstring(L, -1, &sz);
    struct pbc_slice slice;
    slice.buffer = (void*)str;
    slice.len = sz;
    pbc_array_push_slice(array, &slice);
    break;
    }
    case 'M': {
    struct pbc_slice slice;
    if (lua_istable(L,-1)) {
    lua_rawgeti(L,-1,1);
    slice.buffer = lua_touserdata(L,-1);
    lua_rawgeti(L,-2,2);
    slice.len = lua_tointeger(L,-1);
    lua_pop(L,2);
    } else {
    size_t sz = 0;
    const char * buffer = luaL_checklstring(L, -1, &sz);
    slice.buffer = (void *)buffer;
    slice.len = sz;
    }
    pbc_array_push_slice(array, &slice);
    break;
    }
    }
    }


    static void
    push_value(lua_State *L, int type, const char * type_name, union pbc_value *v) {
    switch(type) {
    case PBC_INT:
    lua_pushinteger(L, (int)v->i.low);
    break;
    case PBC_REAL:
    lua_pushnumber(L, v->f);
    break;
    case PBC_BOOL:
    lua_pushboolean(L, v->i.low);
    break;
    case PBC_ENUM:
    lua_pushstring(L, v->e.name);
    break;
    case PBC_BYTES:
    case PBC_STRING:
    lua_pushlstring(L, (const char *)v->s.buffer , v->s.len);
    break;
    case PBC_MESSAGE:
    lua_pushvalue(L, -3);
    lua_pushstring(L, type_name);
    lua_pushlstring(L, (const char *)v->s.buffer , v->s.len);
    lua_call(L, 2 , 1);
    break;
    case PBC_FIXED64: {
    //lua_pushlstring(L, (const char *)&(v->i), 8);
    uint64_t v64 = (uint64_t)(v->i.hi) << 32 | (uint64_t)(v->i.low);
    if (v->i.hi == 0) {
    lua_pushnumber(L, (lua_Number)v64);
    }
    else {
    luaL_u64pushobj(L, v64);
    }
    break;
    }
    case PBC_FIXED32:
    lua_pushlightuserdata(L,(void *)(intptr_t)v->i.low);
    break;
    case PBC_INT64: {
    uint64_t v64 = (uint64_t)(v->i.hi) << 32 | (uint64_t)(v->i.low);
    //lua_pushnumber(L,(lua_Number)(int64_t)v64);
    if (v->i.hi == 0) {
    lua_pushnumber(L, (lua_Number)(int64_t)v64);
    }
    else {
    luaL_i64pushobj(L, (int64_t)v64);
    }
    break;
    }
    case PBC_UINT: {
    uint64_t v64 = (uint64_t)(v->i.hi) << 32 | (uint64_t)(v->i.low);
    if (v->i.hi == 0) {
    lua_pushnumber(L, (lua_Number)v64);
    }
    else {
    luaL_u64pushobj(L, v64);
    }
    //lua_pushnumber(L,(lua_Number)v64);
    break;
    }
    default:
    luaL_error(L, "Unknown type %s", type_name);
    break;
    }
    }

测试:把addressbook.id改成int64,测试代码对应如下修改,输出结果为9223372036854775807LL

1
2
id = "9223372036854775807", -- 修改了pbc底层,会自动序列化成int64格式
-- id = require("int64").new_signed("9223372036854775807"),

游戏中微信支付的接入

发表于 2019-04-12 |

android接入,参考文献

  • 流程:
    1. 客户端向自己的服务端申请订单,服务端同步到微信服务端
    2. 客户端收到订单后,调起微信进行支付
    3. 等微信返回后,会通知客户端,然后根据结果向自己的服务端请求相关信息
    4. 同时服务端会接受微信服务端的支付成功失败通知,也可以向微信服务端查询支付情况
  • android的sdk已经升级过,从sdk变成了opensdk,下载wechat-sdk-android-without-mta-x.x.xjar或者wechat-sdk-android-with-mta-x.x.xjar
  • 引入微信支付依赖,android studio的build.gradle文件中,添加如下依赖即可:

    1
    2
    3
    dependencies {
    compile 'com.tencent.mm.opensdk:wechat-sdk-android-with-mta:+'
    }
  • 配置WXPayEntryActivity,从官网demo中copy一个来即可,必须放到app包下新建一个wxapi包里面

  • AndroidManifest.xml中注册

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <!--wx pay所需权限-->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <!--wx pay-->
    <activity
    android:name=".wxapi.WXPayEntryActivity"
    android:exported="true"
    android:launchMode="singleTop">
    <activity/>
  • 发送支付信息给服务端,自定义处理

  • 跳转微信进行支付,java代码大致如下,需要注意的是需要运行到UI主线程,不然Toast.makeText等操作会崩溃

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    public static void wxPay(final String orderJson)
    {
    mAppActivity.runOnUiThread(new Runnable(){
    @Override
    public void run() {
    try{
    JSONObject json = new JSONObject(orderJson);
    if(null != json){
    PayReq req = new PayReq();
    req.appId = APP_ID; //json.getString("appid");
    req.partnerId = json.optString("partnerid");
    req.prepayId = json.optString("prepayid");
    req.nonceStr = json.optString("noncestr");
    req.timeStamp = json.optString("timestamp");
    req.packageValue = json.optString("package");
    req.sign = json.optString("sign");
    //req.extData = json.optString("extdata"); // optional
    Toast.makeText(mAppActivity, "正在打开微信", Toast.LENGTH_SHORT).show();

    mWXAPI.sendReq(req);
    }else{
    Toast.makeText(mAppActivity, "生成订单失败", Toast.LENGTH_SHORT).show();
    }
    }catch(Exception e){
    Toast.makeText(mAppActivity, "支付失败"+e.getMessage(), Toast.LENGTH_SHORT).show();
    }
    }
    });
    }
  • 微信客户端回调支付结果处理,微信会自动回调到继续IWXAPIEventHandler的WXPayEntryActivity::public void onResp(BaseResp resp),如果在cocos上调用,同样要注意让回调运行在GLThread中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    public static void onWXResp(BaseResp resp) {
    if (resp instanceof SendAuth.Resp) {
    //
    }
    else if (resp instanceof SendMessageToWX.Resp) {
    //
    }
    // com.tencent.mm.opensdk.constants.ConstantsAPI;
    // resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX
    else if (resp instanceof PayResp) {
    final PayResp payResp = (PayResp)resp;
    if (payResp.errCode == 0) {
    mAppActivity.runOnGLThread(new Runnable(){
    @Override
    public void run() {
    onWXpaySuccess("支付成功");
    }
    });
    } else {
    final String resultStatus;
    if (payResp.errCode == -2) {
    resultStatus = "您取消了支付";
    } else {
    resultStatus = "支付失败";
    }
    mAppActivity.runOnGLThread(new Runnable(){
    @Override
    public void run() {
    onWXpayFail(resultStatus);
    }
    });
    }
    }
    }

ios接入,参考文献

  • 接入流程跟android是一样的,区别的是,一般ios游戏为了上AppStore,一般不接带支付功能的sdk,所以用微信去除支付功能的sdk
  • 将解压的微信OpenSDK文件夹拷贝到项目文件夹下,并导入开发环境中。

    1
    2
    3
    4
    libWeChatSDK.a
    WechatAuthSDK.h
    WXApi.h
    WXApiObject.h
  • 并根据官方文档导入依赖库

  • 添加URL Schemes, 点击项目名称,点击“Info”选项卡,在“URL Types”选项中,点击“+”, 在“URL Schemes”中输入微信的注册码
  • 使用URLScheme调起weixin

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    void wxPay(const std::string& orderJson, const std::function<void(bool, const std::string&)>& callback)
    {
    std::string output = "";
    // 隐藏微信地址
    Base64Decode("d2VpeGluOi8vYXBwL3d4NWI5YmZkZGFiNGZiZjQyNi9wYXkvPw==", &output);

    rapidjson::Document document;
    document.Parse(orderJson.c_str());
    std::string partnerid, prepayid, noncestr, timestamp, package, sign, extdata;
    bool bfirst = true;

    if ( document.HasMember("extdata") ) {
    rapidjson::Value& v = document["extdata"];
    if (v.IsString()) {
    extdata = v.GetString();
    if (!extdata.empty()) {
    output += "extData=" + extdata;
    bfirst = false;
    }
    }
    }
    if ( document.HasMember("noncestr") ) {
    rapidjson::Value& v = document["noncestr"];
    if (v.IsString()) {
    noncestr = v.GetString();
    if (noncestr.empty()) { callback(false, "支付失败"); return;}
    if ( bfirst ) {
    bfirst = false;
    output += "nonceStr=" + noncestr;
    } else {
    output += "&nonceStr=" + noncestr;
    }
    }
    }
    if ( document.HasMember("package") ) {
    rapidjson::Value& v = document["package"];
    if (v.IsString()) {
    package = v.GetString();
    package = package.replace(package.find("="), 1, "\%\%3D");
    if (package.empty()) { callback(false, "支付失败"); return;}
    output += "&package=" + package;
    }
    }
    if ( document.HasMember("partnerid") ) {
    rapidjson::Value& v = document["partnerid"];
    if (v.IsString()) {
    partnerid = v.GetString();
    if (partnerid.empty()) { callback(false, "支付失败"); return;}
    output += "&partnerId=" + partnerid;
    }
    }
    if ( document.HasMember("prepayid") ) {
    rapidjson::Value& v = document["prepayid"];
    if (v.IsString()) {
    prepayid = v.GetString();
    if (prepayid.empty()) { callback(false, "支付失败"); return;}
    output += "&prepayId=" + prepayid;
    }
    }
    if ( document.HasMember("timestamp") ) {
    rapidjson::Value& v = document["timestamp"];
    if (v.IsString()) {
    timestamp = v.GetString();
    if (timestamp.empty()) { callback(false, "支付失败"); return;}
    output += "&timeStamp=" + timestamp;
    }
    }
    if ( document.HasMember("sign") ) {
    rapidjson::Value& v = document["sign"];
    if (v.IsString()) {
    sign = v.GetString();
    if (sign.empty()) { callback(false, "支付失败"); return;}
    output += "&sign=" + sign;
    }
    }
    output += "&signType=SHA1";

    setWXpayCallback(callback);
    NSString * pUrl = [NSString stringWithUTF8String:output.c_str()];
    NSURL* url = [NSURL URLWithString:pUrl];
    [[UIApplication sharedApplication] openURL:url];
    }
  • 等待微信回调,头文件中App继承WXApiDelegate,sdk会回调到openURL,但并不会回调到onResp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url
    {
    return [WXApi handleOpenURL:url delegate:self];
    }

    // NOTE: 9.0以后使用新API接口
    - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString*, id> *)options
    {
    if ([url.absoluteString rangeOfString:APP_ID].length) {
    auto callback = getWXpayCallback();
    if ( callback ) {
    // 微信支付,微信回调的时候回回传ret参数,这样来区分支付知否成功
    NSArray *array = [url.absoluteString componentsSeparatedByString:@"ret="];
    NSInteger ret = [array.lastObject integerValue];
    // 调起微信结果返回
    // ret: 0成功支付 -2取消支付 -1其他原因
    if ( ret == 0 ) {
    callback(true, "支付成功");
    } else if ( ret==-2 ) {
    callback(false, "您取消了支付");
    } else {
    callback(false, "支付失败");
    }
    setWXpayCallback(nullptr);
    }
    }
    else {
    return [WXApi handleOpenURL:url delegate:self];
    }
    return YES;
    }

cocos2dx最近遇到的一些注意点

发表于 2019-04-04 |

Bug

  1. Android下异步加载文件的时候会崩溃,CCFileUtils-android.cpp:286
    1
    2
    3
    // Director::getInstance()->getEventDispatcher()->dispatchCustomEvent(EVENT_BEFORE_READ_FILE);
    // 这样修改还是有线程安全的风险,但不会崩溃了
    EngineDataManager::onBeforeReadFile();

注意点

  1. cc.XMLHttpRequest 在ios下对”{}[]”进行了URL转义,会导致服务器无法识别(Java spring可以识别)
  2. 打包资源如果需要修改资源后缀名,那么需要注意Android对assets目录下文件的压缩行为

    1
    2
    3
    4
    5
    6
    7
    8
    9
    /* these formats are already compressed, or don't compress well */
    static const char* kNoCompressExt[] = {
    ".jpg", ".jpeg", ".png", ".gif",
    ".wav", ".mp2", ".mp3", ".ogg", ".aac",
    ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet",
    ".rtttl", ".imy", ".xmf", ".mp4", ".m4a",
    ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2",
    ".amr", ".awb", ".wma", ".wmv"
    };
  3. 打包资源如果需要修改资源后缀名,那么需要注意像骨骼动画等plist和图片是同一个名字,cocos对这些处理不同平台有不一样的表现

  4. 打包资源如果需要修改资源后缀名,声音文件需要特别注意,cocos不是根据文件头判断当前文件是什么格式,而是通过后缀名
  5. 在cocos内部,加载lua脚本,需要注意是否是重新加载
    1
    2
    3
    4
    # 不会重新加载,需要对package.loaded、package.preload删除已经加载的模块
    engine->executeString("require 'lobby.main'");
    # 会重新加载
    engine->executeString("require 'lobby/main'");

其他

  1. 网页引用js生效问题
    1
    2
    3
    4
    <!-- 不生效 -->
    <script src="http://cdn.bootcss.com/jquery/1.10.0/jquery.min.js" />
    <!-- 生效 -->
    <script src="http://cdn.bootcss.com/jquery/1.10.0/jquery.min.js"> </script>

Centos7.5下安装sendmail

发表于 2019-03-05 |

sendmail简介

  • Sendmail是一种多用途、支持多种协议的跨网络电子邮件发送代理软件,由艾瑞克·欧曼(Eric Allman)所开发,于1983年随着BSD 4.1c首次发行。
  • 其他同类软件:postfix、qmail
  • 客户端工具MUA:mailx(http://heirloom.sourceforge.net/mailx.html)

安装和配置

  • 安装

    1
    2
    3
    4
    5
    6
    7
    8
    # sendmail服务端
    yum -y install sendmail sendmail-cf m4
    systemctl start sendmail.service
    # mail客户端
    yum -y install mailx
    # 支持smtp认证
    yum -y install saslauthd
    systemctl start saslauthd
  • sendmail配置

  1. 更换主机名,假设我们的机器名为mymail(本文为mymail),域名为mymail.com

    1
    hostnamectl set-hostname mymail
  2. 修改/etc/hosts

    127.0.0.1 mymail mymail.com

    1
    2
    3
    4
    5
    # cat /etc/hosts
    127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
    ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
    127.0.0.1 mymail mymail.com
    ::1 mymail mymail.com
  3. 修改/etc/mail/sendmail.mc

    1
    2
    3
    4
    5
    # 修改配置项,将下面两行的dnl去掉
    dnl TRUST_AUTH_MECH(\`EXTERNAL DIGEST-MD5 CRAM-MD5 LOGIN PLAIN')dnl
    dnl define(\`confAUTH_MECHANISMS', \`EXTERNAL GSSAPI DIGEST-MD5 CRAM-MD5 LOGIN PLAIN')dnl
    # 将127.0.0.1改为0.0.0.0,意思是任何主机都可以访问Sendmail服务
    DAEMON_OPTIONS(\`Port=smtp, Addr=127.0.0.1, Name=MTA')dnl

生成Sendmail的配置文件:

m4 /etc/mail/sendmail.mc > /etc/mail/sendmail.cf

  1. 将域名加入到local-host-names文件,修改/etc/mail/local-host-names
    1
    mymail.com
  1. 修改/etc/mail/submit.cf,找到行 #Dj$w.Foo.COM,修改为

    1
    2
    3
    4
    # my official domain name
    # ... define this only if sendmail cannot automatically determine your domain
    #Dj$w.Foo.COM
    Djmimvp.com
  2. 修改/etc/mail/acess,添加域名

    1
    Connect: mymail.com           RELAY

执行

1
makemap hash access.db < access

  1. 添加域名MX记录
  2. sendmail可以直接使用 “-r account@domain.com” 参数来以任意源地址发送邮件,但目前主流的邮箱都会将源地址和反向解析IP进行比较,如果解析不到或是解析的IP不匹配,轻则将邮件直接归为SPAM,严重的就直接拒绝接收。
  3. 重启sendmail生效
    1
    systemctl restart sendmail.service

调试和测试

  • 邮件目录结构

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # 邮件执行文件
    /usr/sbin/sendmail
    /usr/bin/mail

    # 邮件配置目录
    /etc/mail/...

    # 邮件日志目录
    /var/log/mail*

    # 未发送邮件列表,可用mailq查看邮件列表
    /var/spool/mqueue/*
  • 建立邮件账号

    1
    adduser mailA -p Pass
  • 在本机上测试,使用xmail工具给mailA发邮件

    1
    echo "hello mymail.com" | mail -v -s "Title" mailA@mymail.com
  • 用其他转发邮箱测试,

    1. 配置/etc/mail.rc,以下以163邮箱示例

      1
      2
      3
      4
      5
      6
      7
      set from=my163mail@163.com
      set smtp=smtps://smtp.163.com:465
      set ssl-verify=ignore
      set nss-config-dir=/root/.certs
      set smtp-auth-user=my163mail@163.com
      set smtp-auth-password=my163mailpassword
      set smtp-auth=login
    2. 导入证书,分别是qq证书和163证书示例

      1
      2
      3
      4
      5
      echo -n | openssl s_client -connect smtp.qq.com:465 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ~/.certs/qq.crt
      certutil -A -n "GeoTrust Global CA" -t "C,," -d ~/.certs -i ~/.certs/qq.crt
      certutil -A -n "GeoTrust SSL CA" -t "C,," -d ~/.certs -i ~/.certs/qq.crt
      certutil -L -d .certs
      certutil -A -n "GeoTrust SSL CA - G3" -t "Pu,Pu,Pu" -d ~/.certs/ -i ~/.certs/qq.crt
1
2
3
4
5
echo -n | openssl s_client -connect smtp.163.com:465 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ~/.certs/163.crt
certutil -A -n "GeoTrust Global CA" -t "C,," -d ~/.certs -i ~/.certs/163.crt
certutil -A -n "GeoTrust SSL CA" -t "C,," -d ~/.certs -i ~/.certs/163.crt
certutil -L -d /root/.certs
certutil -A -n "GeoTrust SSL CA - G3" -t "Pu,Pu,Pu" -d ~/.certs/ -i ~/.certs/163.crt
  1. 发送邮件
    1
    echo "hello mymail.com" | mail -v -s "Title" tomail@163.com

AdultVideoScript7.0搭建教程

发表于 2019-03-05 |

AVS环境要求

  • PHP:建议版本5.6,虽然官方说7.0完全支持PHP7了,但是实际上并不是如此。所以如果不想踩坑,还是老老实实用5.6吧。
  • MYSQL
  • Nginx/Apache
  • 官方文档
    • Linux Server
    • Apache Web Server
      • Support for AcceptPathInfo
      • Support for MultiViews (or mod_rewrite which is slower)
    • MySQL / MySQLi
    • PHP 5.x / 7.x (mod_php/CGI)
      • Support for GD2 Library
      • Support for MySQL / MySQLi
      • Support for Curl
    • Recommended PHP Configuration
      • safe_mode off
      • open_basedir off
      • max_execution_time 7200
      • max_input_time 7200
      • memory_limit (with 1MB more then the maximum video upload size)
      • post_max_size (maximum video upload size)
      • upload_max_size (maximum video upload size)
      • exec() should be allowed
    • PHP CLI 5.x (see above + register_argc_argv ON)
    • FFmpeg (Recommended Version: 3+)

环境配置

  • 一台干净的Ubuntu服务器
  • 安装宝塔

    1
    2
    3
    wget -O install.sh http://download.bt.cn/install/install-ubuntu.sh && sudo bash install.sh
    # 完成后打开管理页面:http://ip:8888
    # 安装AVS的环境:PHP5.6、Mysql、Nginx(建议使用)、phpmyadmin
  • 安装视频插件ffmpeg,官方推荐3.x版本

    1
    2
    3
    4
    5
    6
    cd
    wget https://www.johnvansickle.com/ffmpeg/old-releases/ffmpeg-3.3.4-64bit-static.tar.xz
    tar -xJf ffmpeg-3.3.4-64bit-static.tar.xz
    cd ffmpeg-3.3.4-64bit-static
    cp ffmpeg /usr/bin
    cp ffprobe /usr/bin
  • 下载好AVS代码,根据AVS环境文档配置

安装网站

  • 在宝塔后台建立一个网站,点击网站=>添加站点,按要求添加,其中建立数据库avs7,记录好相应的账号密码
  • 在此站点下增加伪动态配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    location / {
    if (!-f $request_filename){
    set $rule_1 1$rule_1;
    }
    if (!-d $request_filename){
    set $rule_1 2$rule_1;
    }
    if ($rule_1 = "21"){
    rewrite /.* /loader.php last;
    }
    }
  • 导入avs的sql脚本sql/avs.sql

    1
    2
    3
    mysql -u root -p
    mysql>use avs7
    mysql>source sql/avs.sql
  • 源码修改,源码在upload目录,按以下要求修改

    1. Edit include/config.paths.php and set $config[‘BASE_URL’] to your site’s url.
    2. OPTIONAL: If you use a subdirectory for your installion set $config[‘RELATIVE’] to your subdirectory (eg: $config[‘RELATIVE’] = ‘/subdirectory’)!
    3. OPTIONAL: If you use a subdirectory for your installation edit loader.php and set $relative to your subdirectory (eg: $relative = ‘/subdirectory’)!
    4. Create a database. Edit include/config.db.php and set the database information (name, user and password).
    5. Import the sql dump from sql/avs.sql in your database (most likely using phpMyAdmin).
    6. Upload all files and folders from /upload to your server.
    7. Chmod following files and directories to 0777. You can also fix the file/directory permissions from the admin panel: Settings > General > System Check

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      /include/config.local.php
      /cache/frontend
      /cache/backend
      /images/logo
      /images/notice_images
      /images/notice_images/thumbs
      /media/albums
      /media/categories/game
      /media/categories/album
      /media/categories/video
      /media/csv
      /media/games/swf
      /media/games/tmb
      /media/games/tmb/orig
      /media/photos
      /media/photos/tmb
      /media/player/logo
      /media/users
      /media/users/orig
      /media/videos/tmb
      /media/videos/vid
      /media/videos/h264
      /templates/backend/default/analytics/analytics.tpl
      /templates/emails
      /templates/emails/*.tpl
      /templates/frontend/(your_template)/static/*.tpl
      /tmp/albums
      /tmp/avatars
      /tmp/downloads
      /tmp/logs
      /tmp/sessions
      /tmp/thumbs
      /tmp/uploader
      /aembed.sh
    8. Go to www.domain.com/siteadmin (login using: admin/admin) and start configuring your website.

    9. You can use the Google Video Sitemap generator using the link: http://www.yourdomain.com/sitemap.php
    10. Enable non-www to www redirection (domain.com to www.domain.com) by editing the .htaccess file and replacing “domain.com” with your domain name.
  • 访问站点
    后台地址http://xx.com/siteadmin,用户名和密钥都是admin,进入后咱们访问Settings-General-System Check,检查环境能否满意。

其他

  • 这个程序的模板默认会在前台调用一个Google的js,我们需要替换改成国内的地址,不然你的站点在国内打开会非常慢。
    修复路径:templates/frontend/主题名/header.tpl,将里面的:https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js改为:https://cdn.bootcss.com/jquery/1.11.1/jquery.min.js
  • 如果提示open_basedir有问题,那么注释掉网站目录下.user.ini中open_basedir
  • 修改classes/email.class.php:4,添加其他头依赖

    1
    2
    3
    4
    5
    6
    use PHPMailer\PHPMailer\PHPMailer;
    if ( !class_exists('PHPMailer') ) {
    require $config['BASE_DIR']. '/include/phpmailer/PHPMailer.php';
    require $config['BASE_DIR']. '/include/phpmailer/SMTP.php';
    require $config['BASE_DIR']. '/include/phpmailer/Exception.php';
    }
  • 主题模板会调用google的js脚本,需要将他们替换掉,主题目录templates/frontend/主题名,

    • 替换embed.tpl、enter.tpl、header.tpl、view.tpl中的”https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"为"https://cdn.bootcss.com/jquery/1.11.1/jquery.min.js
      “
    • 替换feedback.tpl、invite.tpl、
      grep: img: Is a directory中的”https://www.google.com/recaptcha/api.js"为"https://www.recaptcha.net/recaptcha/api.js"
    • 替换modules/captcha/recaptchalib.php中的”https://www.google.com/recaptcha/api/siteverify"为"https://www.recaptcha.net/recaptcha/api/siteverify"
  • 配置include/config.local.php的recaptcha_site_key和recaptcha_secret_key

PHP环境部署

发表于 2019-02-25 |

Ubuntu系统下手动安装PHP环境

  • 安装php

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    apt-get update

    # 安装php
    apt-get install php

    # 安装cgi以及启动器
    apt-get install php-cgi
    apt-get install spawn-fcgi(或者php-fpm,管理php-cgi的工具)

    # 安装一些库
    apt-get install php-curl
    apt-get install php-mbstring
    apt-get install php-gd
    apt-get install php-mcrypt
    apt-get install php-xml
    apt-get install php-mysql

    # 启动cgi
    spawn-fcgi -a 127.0.0.1 -p 9000 -C 10 -u www-data -f /usr/bin/php-cgi
  • 安装nginx

    1
    2
    3
    apt-get install nginx

    /etc/init.d/nginx restart
  • 安装mysql

    1
    2
    3
    apt-get install mysql-server

    /etc/init.d/mysql restart

PHP其他集成环境

windows环境

  • WAMP,简单易用
  • phpStudy,专业好用
  • UPUPW,专业好用*
  • APMServ

linux环境

  • LNMP 和 LAMP
  • AppServ
  • WDCP

其他工具包

  • OneinStack一键安装包,可以快速安装LNMP、LAMP、LNMT等环境,具体我们可以根据实际的安装进程组合,而且可以一键快速给网站部署Let’s Encrypt免费SSL证书。

    1
    2
    3
    4
    5
    6
    7
    yum -y install wget screen curl python #for CentOS/Redhat
    # apt-get -y install wget screen curl python #for Debian/Ubuntu
    wget http://mirrors.linuxeye.com/oneinstack-full.tar.gz
    tar xzf oneinstack-full.tar.gz
    cd oneinstack
    screen -S oneinstack
    ./install.sh
  • 宝塔

微信支付证书生成

发表于 2019-02-25 |

微信支付证书

  • 微信有三个证书:

    • 客户端公钥证书:apiclient_cert.pem,需要服务端生成
    • 客户端私钥证书:apiclient_key.pem,需要服务端生成
    • 服务端CA证书:rootca.pem,2018年3月6日前生成证书时自带,后续不在提供了,原因是绝大部分操作系统已内置了此证书
  • 证书生成方法

    • 登陆微信商户号
    • 到账户中心->API安全
    • 在API证书下生成证书或者更换证书
    • 生成证书请求
    • 下载并安装证书工具,用证书请求生成证书,妥善保管证书,因无法二次下载(私钥证书必须私有的,微信也不存客户端私钥证书,那自然公钥证书也不需要存了)
    • 生成以下文件
      • apiclient_cert.p12,某些软件环境下使用的格式,如java系
      • apiclient_cert.pem、apiclient_key.pem,另外一些软件环境下使用的格式,如c++、go、php等
      • 证书使用说明.txt,一些说明
  • 到其他网站下载(密码:2wym)一个微信服务端CA证书

win32下cocos对接duilib

发表于 2018-11-28 |

概述

由于需要将cocos的程序在win32下运行,但cocos使用的是系统本身窗口,从游戏角度来说,这个是比较不能接受的。那么肯定需要重绘窗口,简单来说有两种方案:

  • 使用mfc的窗口,重绘来实现
  • 使用例如duilib接管ui来实现

从后续开发简易度来说,使用duilib会比较好些,这里也使用了这个方案。

本文参考了以下方案

强行在MFC窗体中渲染Cocos2d-x 3.6

Duilib接入步骤

  1. 准备工作
  • 下载cocos和对应win32开发工具vs2013,建议使用vs2013,因为下面几个工具确定可以用它来编译
  • 下载glfw,cocos是使用它接管跨平台的窗口创建和管理的,方便对接opengl。需要注意的是,找对cocos对应glfw版本。
  • 下载并安装cmake,建议下载最新版本。
  • 下载并编译duilib
  1. 修改GLFW源码
  • 进入glfw源码目录,打开cmakegui,source code处选择下下来的glfw解压的文件夹,build the binaries选择生成解决方案的文件夹,然后生成对应VS版本的解决方案
  • 进入build the binaries选择生成解决方案的文件夹,打开vs解决方案
  • 打开win32_windows.c,修改createWindow函数,注释处均为修改的地方

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    // 增加三个参数 HWND parent, int px, int py,表示父窗口是谁,以及对应位置
    static int createWindow(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, HWND parent, int px, int py)
    {
    int xpos, ypos, fullWidth, fullHeight;
    WCHAR* wideTitle;
    DWORD style = getWindowStyle(window);
    DWORD exStyle = getWindowExStyle(window);

    // 如果有父窗口,修改窗口属性
    if (parent)
    {
    style = WS_CHILDWINDOW | WS_VISIBLE;
    exStyle = WS_EX_APPWINDOW;
    }

    if (window->monitor)
    {
    GLFWvidmode mode;

    // NOTE: This window placement is temporary and approximate, as the
    // correct position and size cannot be known until the monitor
    // video mode has been set
    _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos);
    _glfwPlatformGetVideoMode(window->monitor, &mode);
    fullWidth = mode.width;
    fullHeight = mode.height;
    }
    else
    {
    xpos = CW_USEDEFAULT;
    ypos = CW_USEDEFAULT;

    if (parent==NULL && wndconfig->maximized)
    style |= WS_MAXIMIZE;

    getFullWindowSize(style, exStyle,
    wndconfig->width, wndconfig->height,
    &fullWidth, &fullHeight);
    }

    // 如果有父窗口,修改窗口位置
    if (parent)
    {
    xpos = px;
    ypos = py;
    }

    wideTitle = _glfwCreateWideStringFromUTF8Win32(wndconfig->title);
    if (!wideTitle)
    {
    _glfwInputError(GLFW_PLATFORM_ERROR,
    "Win32: Failed to convert window title to UTF-16");
    return GLFW_FALSE;
    }

    window->win32.handle = CreateWindowExW(exStyle,
    _GLFW_WNDCLASSNAME,
    wideTitle,
    style,
    xpos, ypos,
    fullWidth, fullHeight,
    parent, // No parent window
    NULL, // No window menu
    GetModuleHandleW(NULL),
    NULL);

    free(wideTitle);

    //...
    //
    }
  • 打开win32_windows.c,修改_glfwPlatformCreateWindow函数,对接createWindow函数的参数,并修改对应的头文件internal.h

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    int _glfwPlatformCreateWindow(_GLFWwindow* window,
    const _GLFWwndconfig* wndconfig,
    const _GLFWctxconfig* ctxconfig,
    const _GLFWfbconfig* fbconfig,
    HWND parent, int px, int py)
    {
    //...
    //
    if (!createWindow(window, wndconfig, parent, px, py))
    return GLFW_FALSE;

    //...
    //
    if (!createWindow(window, wndconfig, parent, px, py))
    return GLFW_FALSE;
    //...
    //
    }
  • glfw3.h,修改接口

    1
    2
    3
    4
    5
    // 为了兼容,保留老接口
    GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share);
    // 增加新接口
    GLFWAPI GLFWwindow* glfwCreateWindowEx(int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share,
    intptr_t parent, int px, int py);
  • windows.h,修改接口实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share)
    {
    return glfwCreateWindowEx(width, height, title, monitor, share, 0, 0, 0);
    }

    GLFWAPI GLFWwindow* glfwCreateWindowEx(int width, int height,
    const char* title,
    GLFWmonitor* monitor,
    GLFWwindow* share,
    intptr_t parent, int px, int py)
    {
    //...
    //

    // Open the actual window and create its context
    if (!_glfwPlatformCreateWindow(window, &wndconfig, &ctxconfig, &fbconfig, (HWND)parent, px, py))
    {
    glfwMakeContextCurrent((GLFWwindow*) previous);
    glfwDestroyWindow((GLFWwindow*) window);
    return NULL;
    }

    //...
    //
    }
  • 重新编译,然后glfw3.h和glfw3.lib复制到Cocos2dx\external\glfw3\include\win32和E:\daclient\Cocos2dx\external\glfw3\prebuilt\win32目录下

  1. 修改对应的cocos源码
  • cocos是使用glfwCreateWindow创建窗口的,找到对应位置,在CCGLViewImpl-desktop.cpp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // 增加了三个参数,对应调用此函数的地方也要做响应修改
    bool GLViewImpl::initWithRect(const std::string& viewName, Rect rect, float frameZoomFactor, bool resizable,
    HWND parent, int px, int py)
    {
    //
    //...

    // 去掉标题栏
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
    glfwWindowHint(GLFW_DECORATED, GL_FALSE); // 这个样式实际上去掉了系统提供的默认标题栏。所有窗口区域都为客户区
    #endif

    int neededWidth = rect.size.width * _frameZoomFactor;
    int neededHeight = rect.size.height * _frameZoomFactor;

    // glfwCreateWindowEx替换glfwCreateWindow
    _mainWindow = glfwCreateWindowEx(neededWidth, neededHeight, _viewName.c_str(), _monitor, nullptr,
    (intptr_t)parent, px, py);

    //
    // ...
    }
  • cocos的Application::getInstance()->run();会阻塞线程,但之后线程会被duilib主窗口接管,所以渲染主循环会放到时钟中去,那么需要将run函数拆分。找到CCApplication-win32.cpp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    // 新增三个函数
    int Application::loopinit()
    {
    PVRFrameEnableControlWindow(false);

    initGLContextAttrs();

    // Initialize instance and cocos2d.
    if (!applicationDidFinishLaunching())
    {
    return 1;
    }

    auto director = Director::getInstance();
    auto glview = director->getOpenGLView();

    // Retain glview to avoid glview being released in the while loop
    glview->retain();
    _loopinit = true;

    return 0;
    }

    int Application::looponce()
    {
    if (!_loopinit) return 1;

    auto director = Director::getInstance();
    auto glview = director->getOpenGLView();

    director->mainLoop();
    glview->pollEvents();

    return 0;
    }

    int Application::loopfini()
    {
    if (!_loopinit) return 1;

    auto director = Director::getInstance();
    auto glview = director->getOpenGLView();

    if (glview->isOpenGLReady())
    {
    director->end();
    director->mainLoop();
    director = nullptr;
    }

    glview->release();
    _loopinit = false;

    return 0;
    }
  1. 引入duilib,根据duilib的例子,创建duilib窗口,建议把duilib相关的内容放到单独的cpp文件,不与cocos的文件有牵扯,容易出类库包含不兼容问题。
    1
    2
    3
    4
    // duilib窗口对应函数
    HWND DuiWinCreate(HINSTANCE hInstance);
    void DuiWinShow();
    void DuiWinRun();

duilib窗口的属性配置

duilib库有一些bug,如Dialog的domodel返回值错误;PostQuitMessage(0L)有时候不能退出

  1. 修改主工程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// 修改main.cpp

#include "main.h"

#include "DuiWin.h"
#include "AppDelegate.h"
USING_NS_CC;

#define USE_WIN32_CONSOLE
#define CC_LOOPTIMER 101

// 声明时钟回调函数
void CALLBACK WinSunTimerProc(HWND hWnd, UINT nMsg, UINT nTimerid, DWORD dwTime);

//创建一个完整的窗口需要经过四个步骤:设计一个窗口类;注册窗口类;创建窗口;显示及更新窗口
int WINAPI _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);

HICON hIcon = ::LoadIcon(hInstance, MAKEINTRESOURCE(MAINICON));

#ifdef USE_WIN32_CONSOLE
AllocConsole();
freopen("CONIN$", "r", stdin);
freopen("CONOUT$", "w", stdout);
freopen("CONOUT$", "w", stderr);
#endif
HWND hwnd = DuiWinCreate(hInstance);

AppDelegate app;
app.m_parent = hwnd;
Application::getInstance()->loopinit();
SetTimer(hwnd, CC_LOOPTIMER, 1, WinSunTimerProc);

DuiWinShow();
DuiWinRun();

KillTimer(hwnd, CC_LOOPTIMER);
Application::getInstance()->loopfini();
/*AppDelegate app;
app.m_parent = NULL;
Application::getInstance()->run();*/

#ifdef USE_WIN32_CONSOLE
FreeConsole();
#endif

return 0;
}

void CALLBACK WinSunTimerProc(HWND hWnd, UINT nMsg, UINT nTimerid, DWORD dwTime)
{
if (nTimerid == CC_LOOPTIMER)
{
Application::getInstance()->looponce();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
// 对应创建窗口的地方
bool AppDelegate::applicationDidFinishLaunching()
{
//
// ...

#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
glview = GLViewImpl::createWithRect("168game", Rect(0, 0, 1280, 720), 1.0f, false, HWND(m_parent),0, 24);
#else

//
// ...
}
  1. 至此已经对接完成,本方案不会影响ios和android包的实现。

  2. 其他

  • 显示应用在系统状态的图片,加载一个mfc资源,使用HICON hIcon = ::LoadIcon(hInstance, MAKEINTRESOURCE(MAINICON));即可
  • 使用自定义鼠标cur,似乎不能实现,duilib使用的是系统鼠标,并做一些自动切换

以太坊私有节点部署

发表于 2018-09-17 |

准备工作,参考上一篇文章

  • 已经准备好服务器
  • 已经安装好geth工具链

结构设计

  • 以太坊支持两种共识机制:POW和POA,使用POW没有足够的算力,会丧失自主权,所以私链一般使用POA
  • 总体结构为:1个bootnode、3个挖矿节点、2个rpc节点
    • 1个bootnode,挖矿节点和rpc节点都从bootnode进入到私有链中,比直接去相互连接更加方便
    • 3个挖矿节点,可以容错一个节点停机,因为需要半数以上的节点存活才能继续挖矿
    • 2个rpc节点,也是为了容错

部署

  1. 启动bootnode节点

    1
    2
    3
    4
    5
    # 生成bootnode的key,会在当前目录下生成string对应的私钥文件
    bootnode -genkey string
    # 启动bootnode,string指向key filename
    bootnode -nodekey string
    # 启动后会看到有关bootnode的日志,如“enode://xxxxxxx@ip:port”
  2. 生成一批帐号,3个帐号用来挖矿,1个存储货币,以及用于其他业务帐号

    1
    2
    # 在当前目录下创建帐号,根据提示输入钱包密码,在本地会在keystore目录下生成一个UTC--xxx的钱包文件
    ./geth account new
  3. 生成创世配置,使用以太坊自带的puppeth工具,

    1
    2
    3
    4
    5
    6
    puppeth
    # 配置配置名字,如输入x_poa就会生成x_poa.json文件
    # 设置共识机制POA,对应关键词是“clique”
    # 配置networkid
    # 允许挖矿的帐号地址
    # 配置发行货币量,以及货币存到哪个帐号上
  4. 启动挖矿节点

    1
    2
    3
    4
    # 初始化节点
    geth --datadir /miner0.d init x_poa.json
    # 启动节点
    geth --datadir /miner0.d --networkid 1024 --bootnodes enode://83789b30dd3d091bad865c1ab14c12bf28eabf7babe291965e2e9beffaa79f2e55fb2e63f5ff8f7f25483fe85b8803d0101dbf87b11e46e614d6ddc7100955c2@172.20.0.56:30301 --unlock 0x6df176a7f059223715121d7cd3c75b87f46cdca2 --password node0.d/.password --mine --minerthreads=1 --gasprice 1 console
  5. 启动RPC节点

    1
    2
    3
    4
    # 初始化节点
    geth --datadir /node0.d init x_poa.json
    # 启动节点
    ./geth --datadir /node0.d --networkid 1024 --bootnodes enode://83789b30dd3d091bad865c1ab14c12bf28eabf7babe291965e2e9beffaa79f2e55fb2e63f5ff8f7f25483fe85b8803d0101dbf87b11e46e614d6ddc7100955c2@172.20.0.56:30301 --rpc --rpcaddr 0.0.0.0 --rpccorsdomain * --rpcapi eth,net,web3 --ws --wsaddr 0.0.0.0 --wsorigins * --wsapi eth,net,web3 --ipcdisable --gasprice 1 console

POA共识添加和删除旷工

1
2
3
4
5
6
# 查看挖矿节点
admin.peers()
# 投票添加节点,半数以上添加,方可加入
clique.propose(signer_address,true)
# 投票删除节点,半数以上删除,方可删除
clique.discard(signer_address)

以太坊全节点部署

发表于 2018-09-17 |

选型

目前有三类节点是使用最多的,go-ethereum(55%)、parity(22%)、ethereumjs-devp2p(15%),go-ethereum是官方版本,且占比最大,这里就选择go-ethereum来部署

为什么要部署全节点

以太坊有两种节点:全节点、轻节点。全节点存储区块链的全部状态,需要350G的存储空间和8G以上的内存。轻节点只存储最小量的状态,只下载区块头和与其交易相关的默克尔树“分支”,它只需要几百M存储空间和128-512M内存。当发起转账、合约部署的时候,轻节点是不能独立完成的,他需要借助其他全节点以他的名义来操作。这一切本省并没有什么问题,但当遇到交易量急剧上升,区块链升级的时候,它会找不到合适的全节点给它使用。因此我们再业务上使用的时候,建议使用全节点。

在Linux上部署

  • 从官网下载合适的版本(建议选择带tools的版本),并解压到/usr/local/bin

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    [root@blockchain-server01 data]# tar -xzvf geth-alltools-linux-amd64-1.8.15-89451f7c -C /usr/local/bin/ -f
    [root@blockchain-server01 data]# geth version
    Geth
    Version: 1.8.15-stable
    Git Commit: 89451f7c382ad2185987ee369f16416f89c28a7d
    Architecture: amd64
    Protocol Versions: [63 62]
    Network Id: 1
    Go Version: go1.10.4
    Operating System: linux
    GOPATH=
    GOROOT=/home/travis/.gimme/versions/go1.10.4.linux.amd64
  • 启动参数:RCP接口

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    API AND CONSOLE OPTIONS:
    --rpc Enable the HTTP-RPC server
    --rpcaddr value HTTP-RPC server listening interface (default: "localhost")
    --rpcport value HTTP-RPC server listening port (default: 8545)
    --rpcapi value API's offered over the HTTP-RPC interface "db,eth,net,web3,miner,personal"
    --ws Enable the WS-RPC server
    --wsaddr value WS-RPC server listening interface (default: "localhost")
    --wsport value WS-RPC server listening port (default: 8546)
    --wsapi value API's offered over the WS-RPC interface
    --wsorigins value Origins from which to accept websockets requests
  • 启动参数:全局设置

    1
    2
    3
    ETHEREUM OPTIONS:
    --datadir "/Users/zzs/Library/Ethereum" Data directory for the databases and keystore
    --syncmode "fast" Blockchain sync mode ("fast", "full", or "light")
  • 启动参数:挖矿设置

    1
    2
    MINER OPTIONS:
    --mine Enable mining
  • 启动参数:网络属性

    1
    2
    NETWORKING OPTIONS:
    --port value Network listening port (default: 30303)
  • 启动命令,将geth运行到后台,/data/eth_data用来存放以太坊的数据;启动完毕后在/data/eth_data会产生一个geth.ipc文件,用于进程间的ipc通讯

    1
    2
    mkdir /data/eth_data
    nohup geth --datadir /data/eth_data --syncmode full --rpc --rpcaddr 0.0.0.0 --rpccorsdomain "*" --rpcapi eth,net,web3 --ws --wsaddr 0.0.0.0 --wsorigins "*" --wsapi eth,net,web3 &
  • 关闭命令

    1
    2
    # 或者kill 进程id
    kill geth

本地API操作

  • 启动命令行

    1
    2
    cd /data/eth_data
    geth attach geth.ipc
  • 命令行支持的API,这个可以在启动参数上设置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    admin:1.0 
    debug:1.0
    eth:1.0
    ethash:1.0
    miner:1.0
    net:1.0
    personal:1.0
    rpc:1.0
    txpool:1.0
    web3:1.0
  • 查看同步进程,当eth.syncing返回false的时候表示没开始同步,或者同步已经完毕

    1
    2
    3
    4
    5
    cd /data/eth_data
    geth attach geth.ipc
    # 获取区块高度
    > eth.blockNumber
    > eth.syncing
12…4

Lucus Gu

32 日志
2 标签
RSS
© 2017 — 2019 Lucus Gu
由 Hexo 强力驱动
|
主题 — NexT.Mist v5.1.4