iOS端手游和c++交互 lua和c++交互

进了一家新的手游公司,坐标杭州,因为之前是做发行做了一段时间,才知道互联网真正赚钱的是手游。所以从武汉来杭州就一心想找一家手游公司,从而深入了解一下iOS工程师能为手游做多少工作。其实要做的工作并没有多少,主要是给手游(无论是unity写的还是cocos写的)套壳子。
工作内容如下:
1、对接渠道方的sdk,现在对接的sdk包括国服的飞鱼sdk(发行萝卜保卫战),日服的b站游戏sdk(坑很多,尤其是外服),韩服的网易sdk(文档写的非常全面,流程复杂),台服的心动sdk(中规中矩)
2、写oc和c++的交互桥接文件,也是我要在本bolg里着重说的一点,写c++和lua的交互桥接文件。
3、优化内存,找到内存泄漏的地方,大概率都是c++代码。

oc和c++的交互桥接文件

我拿韩服和网易sdk的桥接文件举例:
新建C++File
.hpp:本质就是将.cpp的实现代码混入.h头文件当中,定义与实现都包含在同一文件,则该类的调用者只需要include该.hpp文件即可,无需再将cpp加入到project中进行编译。而实现代码将直接编译到调用者的obj文件中,不再生成单独的obj,采用hpp将大幅度减少调用project中的cpp文件数与编译次数,也不用再发布lib与dll文件,因此非常适合用来编写公用的开源库。

hpp的优点不少,但是编写中有以下几点要注意:
1、是Header Plus Plus的简写。(.h和.hpp就如同.c和.cpp似的)
2、与.h类似,.hpp是C++程序头文件格式。
3、是VCL专用的头文件,已预编译。
4、是一般模板类的头文件。
5、一般来说,.h里面只有声明,没有实现,而.hpp里声明实现都有,后者可以减少.cpp的数量。
6、.h里面可以有using namespace std,而.hpp里则无。
7、不可包含全局对象和全局函数。

由于.hpp本质上是作为.h被调用者include的,所以当hpp文件中存在全局对象或者全局函数,而该hpp被多个调用者include时,将在链接时导致符号重定义错误。要避免这种情况,需要去除全局对象,将全局函数封装为类的静态方法。

.mm :源代码文件。带有这种扩展名的源代码文件,除了可以包含Objective-C和C代码以外还可以包含C++代码。仅在你的Objective-C代码中确实需要使用C++类或者特性的时候才用这种扩展名

首先贴上.hpp头文件:

#ifndef NTSdkBridge_hpp
#define NTSdkBridge_hpp#include <stdio.h>
#include <string>
#include <vector>
#include <unordered_map>
typedef std::vector<std::string> payProductList;class NTSdkBridge {public:enum class EventType{INIT,LOGIN,LOGOUT,PAY,SHARE,};static NTSdkBridge* getInstance();
//    void init(std::string gameRegion, std::string gameVersion, std::string gameLanguage, std::string engineVersion, std::string resourceVersion);void login();void logincallback(int code);void logout();void switchAccount();void traceAccount(std::string serverid, int level);void bugReport(std::string serverid, std::string nickname, std::string clientversion);void userCenter(std::string serverid, std::string nickname, std::string clientversion);void storeReview();std::string getChannelID();//支付void queryWithProducts(payProductList* list);float getProductAmount(std::string id);std::string getProductCurrency(std::string id);std::string getProductDisplayPrice(std::string id);void payWithProductID(std::string productid, std::string serverid, std::string ext, int level);void paycallback(int code);void paycallback(int code, std::string productid);void eventTrack(std::string event);void registerEventHandler(EventType event, int handler);//分享void share(std::string image, std::string link, std::string msg);void sharecallback(int code);private:NTSdkBridge();~NTSdkBridge();void removeDeprecatedHandler(int &handler);private:static NTSdkBridge* instance;int _inithandler = 0;int _loginhandler = 0;int _logouthandler = 0;int _payhandler = 0;int _sharehandler = 0;std::unordered_map<std::string, std::string> _cached_products;
};
#endif /* NTSdkBridge_hpp */

头文件包括几部分:
1、public方法:
枚举的EventType:回调event
函数声明
2、private方法:
处理回调的handler
私有函数声明

.mm文件:

#include "NTSdkBridge.hpp"
#include <stdio.h>
#import "RootViewController.h"
#include "cocos2d.h"
#include "CCLuaEngine.h"#include <sys/param.h>
#include <sys/mount.h>
#include <ui/UIVideoPlayer.h>
#include <NtUniSdkNetEaseGlobal/NtUniSdk.h>@interface NTAgent : NSObject
-(void) loginNotification:(NSNotification *)notification;
-(void) finishInitNotification:(NSNotification *)notification;
-(void) registerNotification:(NSNotification *)notification;
-(void) logoutNotification:(NSNotification *)notification;
-(void) closeManagerViewNotification:(NSNotification *)notification;
-(void) closeFFViewNotification:(NSNotification *)notification;
-(void) getProductInfoNotification:(NSNotification *)notification;
-(void) userManagerViewHasNewMessage:(NSNotification *)notification;
-(void) init:(void*)ntbridge;
@end
@implementation NTAgent
{NTSdkBridge * _bridge;
}-(void) init:(void *)ntbridge{_bridge = (NTSdkBridge *)ntbridge;//初始化通知[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(finishInitNotification:) name:NT_NOTIFICATION_FINISH_INIT object:nil];//监听注册通知[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(registerNotification:) name:NT_NOTIFICATION_REGISTER object:nil];//注销通知[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(logoutNotification:) name:NT_NOTIFICATION_LOGOUT object:nil];//监听登录通知[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(loginNotification:) name:NT_NOTIFICATION_LOGIN object:nil];//管理视图关闭通知[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(closeManagerViewNotification:) name:NT_NOTIFICATION_CLOSE_MANAGERVIEW object:nil];//支付结束通知[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(closeFFViewNotification:) name:NT_NOTIFICATION_CLOSE_FFVIEW object:nil];//成功获取商品信息[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(getProductInfoNotification:) name:NT_NOTIFICATION_FINISH_GET_PRODUCT_INFO object:nil];//用户界面有新的消息[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(userManagerViewHasNewMessage:) name:NT_NOTIFICATION_NEW_MESSAGE object:nil];}-(void)finishInitNotification:(NSNotification *)notification{NSLog(@"finishInitNotification.");NSDictionary *dict = notification.userInfo;if (dict) {//有字典信息,获取dict中的各个字段信息,根据情况进行处理//ex:NSString *value01 = dict[@"key-01"];}else {//没有字典信息,不用处理传入的notification}//处理初始化完成的事情CCLOG("init success");_bridge->initCallBack(0);}-(void)registerNotification:(NSNotification *)notification{NSLog(@"[NtUniSdk] Notification registerNotification.");
}
//注销通知处理,在用户管理界面登出后能收到这个回调
-(void)logoutNotification:(NSNotification *)notification{_bridge->logoutCallBack(0);
}
-(void)closeManagerViewNotification:(NSNotification *)notification{NSLog(@"[NtUniSdk] Notification closeManagerView.");
}
-(void)closeFFViewNotification:(NSNotification *)notification{NSLog(@"[NtUniSdk] Notification closeFFViewNotification.");
}
-(void)getProductInfoNotification:(NSNotification *)notification{}
-(void)userManagerViewHasNewMessage:(NSNotification *)notification{}
-(void)loginNotification:(NSNotification *)notification{NSLog(@"loginNotification.");//NSLog(@"LOGIN_TYPE : %d",[[NtSdkMgr getInst] getPropInt:LOGIN_TYPE default:-1]);NSDictionary *dict = notification.userInfo;NSLog(@"");if ([dict[NT_NOTIFICATION_INFO_LOGIN_STATE] isEqual: NT_NOTIFICATION_INFO_LOGIN_OK]) {NSLog(@"%@",[NSString stringWithFormat:@"[NtUniSdk] Notification login.\nLS: %i\nUID:%@\nSESSION:%@\n",[[NtSdkMgr getInst] hasLogin],[[NtSdkMgr getInst] getPropStr:NT_UID],[[NtSdkMgr getInst] getPropStr:NT_SESSION]]);_bridge->logincallback(0);}else if ([dict[NT_NOTIFICATION_INFO_LOGIN_STATE] isEqual: NT_NOTIFICATION_INFO_LOGIN_FAILED]) {NSLog(@"检查下netease.data文件中的appid是否填写正确");[self msg:@"登录失败"];_bridge->logincallback(1);//参照下方登录失败错误码,可以弹一些提示信息}else if ([dict[NT_NOTIFICATION_INFO_LOGIN_STATE] isEqual: NT_NOTIFICATION_INFO_LOGIN_CANCEL]) {[self msg:@"登录取消"];_bridge->logincallback(2);}else if ([dict[NT_NOTIFICATION_INFO_LOGIN_STATE] isEqual: NT_NOTIFICATION_INFO_BIND_OK]) {[self msg:@"绑定成功"];}NSString *sauth = [[NtSdkMgr getInst] getPropStr:NT_SAUTH_STR];NSLog(@"计费sauth=%@", sauth);sauth = [[NtSdkMgr getInst] getPropStr:NT_SAUTH_JSON];NSLog(@"计费sauth_json=%@", sauth);[[NtSdkMgr getInst] setProp:NT_USERINFO_UID as:@"uid123"];
}
//弹提示
- (void)msg:(NSString*) str
{dispatch_async(dispatch_get_main_queue(), ^{UIAlertView * alertA= [[UIAlertView alloc] initWithTitle:@"UniSDK 提示" message:str delegate:self cancelButtonTitle:@"哦哦" otherButtonTitles: nil];[alertA show];});
}
@end
NTSdkBridge* NTSdkBridge::instance = NULL;USING_NS_CC;NTSdkBridge::NTSdkBridge()
:_loginhandler(0),
_payhandler(0),
_sharehandler(0) {}NTSdkBridge::~NTSdkBridge() {}NTSdkBridge* NTSdkBridge::getInstance() {if (instance == NULL) {instance = new NTSdkBridge();}return instance;
}void NTSdkBridge::login() {[[NtSdkMgr getInst] ntLogin];
}void NTSdkBridge::logout() {[[NtSdkMgr getInst] ntLogout];
}void NTSdkBridge::switchAccount() {}void NTSdkBridge::traceAccount(std::string serverid, int level) {}void NTSdkBridge::bugReport(std::string serverid, std::string nickname, std::string clientversion) {}void NTSdkBridge::userCenter(std::string serverid, std::string nickname, std::string clientversion) {}void NTSdkBridge::storeReview() {}std::string NTSdkBridge::getChannelID() {return "ios_kr_hqxy";
}//获取手机剩余空间 单位MB
std::string NTSdkBridge::getFreeDiskSpaceInBytes(){struct statfs buf;unsigned long long freeSpace = -1;if (statfs("/var", &buf) >= 0) {freeSpace = (unsigned long long)(buf.f_bsize * buf.f_bavail);}NSString *str = [NSString stringWithFormat:@"%0.2lld ",freeSpace/1024/1024];return [str UTF8String];
}void NTSdkBridge::queryWithProducts(payProductList* list) {NSMutableArray *products = [[NSMutableArray alloc] init];for (int i = 0; i < list->size(); i++) {[products addObject:[NSString stringWithUTF8String:list->at(i).c_str()]];}}float NTSdkBridge::getProductAmount(std::string id) {return -1;
}std::string NTSdkBridge::getProductCurrency(std::string id) {return "";
}std::string NTSdkBridge::getProductDisplayPrice(std::string id) {auto iter = _cached_products.find(id);if (iter != _cached_products.end()) {//CCLOG("GetProductPrice: %s", iter->second.c_str());return iter->second;}return "";
}void NTSdkBridge::payWithProductID(std::string productid, std::string serverid, std::string ext, int level) {}void NTSdkBridge::eventTrack(std::string event) {}void NTSdkBridge::removeDeprecatedHandler(int &handler) {if (handler == 0)return;LuaEngine::getInstance()->removeScriptHandler(handler);handler = 0;
}void NTSdkBridge::registerEventHandler(EventType event, int handler) {switch(event) {case EventType::INIT: {removeDeprecatedHandler(_inithandler);_inithandler = handler;break;}case EventType::LOGIN: {removeDeprecatedHandler(_loginhandler);_loginhandler = handler;break;}case EventType::PAY: {removeDeprecatedHandler(_payhandler);_payhandler = handler;break;}case EventType::SHARE: {removeDeprecatedHandler(_sharehandler);_sharehandler = handler;break;}case EventType::LOGOUT: {removeDeprecatedHandler(_logouthandler);_logouthandler = handler;break;}default:break;}
}
void NTSdkBridge::initCallBack(int code) {if (_inithandler == 0)return;LuaStack* stack = LuaEngine::getInstance()->getLuaStack();stack->pushInt(code);stack->executeFunctionByHandler(_inithandler, 1);stack->clean();
}void NTSdkBridge::logincallback(int code) {if (_loginhandler == 0)return;LuaStack* stack = LuaEngine::getInstance()->getLuaStack();stack->pushInt(code);stack->executeFunctionByHandler(_loginhandler, 1);stack->clean();
}void NTSdkBridge::logincallback(int code, int uid, std::string sid, bool isGuest, bool isBindPhoneNum) {if (_loginhandler == 0)return;LuaStack* stack = LuaEngine::getInstance()->getLuaStack();stack->pushInt(code);stack->pushInt(uid);stack->pushString(sid.c_str());stack->pushBoolean(isGuest);stack->pushBoolean(isBindPhoneNum);stack->executeFunctionByHandler(_loginhandler, 5);stack->clean();
}void NTSdkBridge::paycallback(int code) {if (_payhandler == 0)return;LuaStack* stack = LuaEngine::getInstance()->getLuaStack();stack->pushInt(code);stack->executeFunctionByHandler(_payhandler, 1);stack->clean();
}void NTSdkBridge::paycallback(int code, std::string productid) {if (_payhandler == 0)return;LuaStack* stack = LuaEngine::getInstance()->getLuaStack();stack->pushInt(code);stack->pushString(productid.c_str());stack->executeFunctionByHandler(_payhandler, 2);stack->clean();
}void NTSdkBridge::logoutCallBack(int code) {if (_logouthandler == 0)return;LuaStack* stack = LuaEngine::getInstance()->getLuaStack();stack->pushInt(code);stack->executeFunctionByHandler(_logouthandler, 1);stack->clean();
}
void NTSdkBridge::share(std::string image, std::string link, std::string msg) {NSURL* url = [NSURL URLWithString:[NSString stringWithUTF8String:link.c_str()]];}void NTSdkBridge::sharecallback(int code) {if (_sharehandler == 0)return;LuaStack* stack = LuaEngine::getInstance()->getLuaStack();stack->pushInt(code);stack->executeFunctionByHandler(_sharehandler, 1);stack->clean();
}

.mm文件包含几个内容:
1、bridge类扩展
包括init函数供oc调用,基本都是在appdelegate里调用,sdk的通知监听和通知的实现。
2、bridge单例的初始化,public函数实现
3、注册回调handler
4、回调实现,实现和lua的交互(下面会提到lua和c++的交互桥接文件)

lua和C++的交互

因为我游是lua写的脚本,大多数游戏都是lua脚本,也有js脚本。所以我们要在壳子里写lua和c++交互的桥接文件。

ntsdk_lua_binding.hpp:

#ifndef ntsdk_lua_binding_hpp
#define ntsdk_lua_binding_hpp#ifdef __cplusplus
extern "C" {#endif
#include "tolua++.h"
#ifdef __cplusplus
}
#endifTOLUA_API int register_ntsdk_luabinding(lua_State* L);
#endif /* ntsdk_lua_binding_hpp */

tolua++.h文件,网上有很多成熟的tolua的工具库,大概都是”tolua_isnumber“,这种。
最后要注册这个桥接文件,在lua里调用。
tolua++需要将 c/c++ 中的类型,变量,函数,对象导出到lua

ntsdk_lua_binding.cpp:

#include "ntsdk_lua_binding.hpp"
#include "NTSdkBridge.hpp"
#include "tolua_fix.h"
#include "LuaBasicConversions.h"
#include "CCLuaEngine.h"
#include <string>int lua_ntsdk_login(lua_State* toluaS) {NTSdkBridge::getInstance()->login();return 0;
}int lua_ntsdk_switchaccount(lua_State* toluaS) {NTSdkBridge::getInstance()->switchAccount();return 0;
}int lua_ntsdk_traceaccount(lua_State* toluaS) {if (lua_gettop(toluaS) < 2 || !lua_isstring(toluaS, 1) || !lua_isnumber(toluaS, 2)) {CCLOG("Wrong Args In traceAccount!!!!");return 0;}std::string serverid = tolua_tocppstring(toluaS, 1, "");int level = lua_tointeger(toluaS, 2);NTSdkBridge::getInstance()->traceAccount(serverid, level);return 0;
}int lua_ntsdk_bugreport(lua_State* toluaS) {if (lua_gettop(toluaS) < 3 || !lua_isstring(toluaS, 1) || !lua_isstring(toluaS, 2) || !lua_isstring(toluaS, 3)) {CCLOG("Wrong Args In bugReport!!!!!");return 0;}std::string serverid = tolua_tocppstring(toluaS, 1, "");std::string nickname = tolua_tocppstring(toluaS, 2, "");std::string clientversion = tolua_tocppstring(toluaS, 3, "");NTSdkBridge::getInstance()->bugReport(serverid, nickname, clientversion);return 0;
}int lua_ntsdk_usercenter(lua_State* toluaS) {if (lua_gettop(toluaS) < 3 || !lua_isstring(toluaS, 1) || !lua_isstring(toluaS, 2) || !lua_isstring(toluaS, 3)) {CCLOG("Wrong Args In bugReport!!!!!");return 0;}std::string serverid = tolua_tocppstring(toluaS, 1, "");std::string nickname = tolua_tocppstring(toluaS, 2, "");std::string clientversion = tolua_tocppstring(toluaS, 3, "");NTSdkBridge::getInstance()->userCenter(serverid, nickname, clientversion);return 0;
}int lua_ntsdk_storereview(lua_State* toluaS) {NTSdkBridge::getInstance()->storeReview();return 0;
}int lua_ntsdk_getchannelid(lua_State* toluaS) {std::string channelid = NTSdkBridge::getInstance()->getChannelID();lua_pushstring(toluaS, channelid.c_str());return 1;
}int lua_ntsdk_querywithproducts(lua_State* toluaS) {if (lua_gettop(toluaS) < 1 || (!lua_istable(toluaS, 1))) {CCLOG("Wrong Params For queryWithProducts!!!!!");return 0;}payProductList list;size_t len = lua_objlen(toluaS, 1);if (len <= 0) {return 0;}for (int i = 0; i < len; i++) {lua_pushnumber(toluaS, i+1);lua_gettable(toluaS, 1);if (lua_isnil(toluaS, -1)) {lua_pop(toluaS, 1);continue;}if (!lua_isstring(toluaS, -1)) {lua_pop(toluaS, 1);continue;}std::string value = tolua_tocppstring(toluaS, -1, "");list.push_back(value);lua_pop(toluaS, 1);}NTSdkBridge::getInstance()->queryWithProducts(&list);return 0;
}int lua_ntsdk_paywithproductid(lua_State* toluaS) {if (lua_gettop(toluaS) < 4 || !lua_isstring(toluaS, 1) || !lua_isstring(toluaS, 2) || !lua_isstring(toluaS, 3) || !lua_isnumber(toluaS, 4)) {CCLOG("Wrong Args For payWithProductID!!!!");return 0;}std::string productid = tolua_tocppstring(toluaS, 1, "");std::string serverid = tolua_tocppstring(toluaS, 2, "");std::string ext = tolua_tocppstring(toluaS, 3, "");int level = lua_tointeger(toluaS, 4);NTSdkBridge::getInstance()->payWithProductID(productid, serverid, ext, level);return 0;
}int lua_ntsdk_registereventhandler(lua_State* toluaS) {if (lua_gettop(toluaS) < 2 || !lua_isnumber(toluaS, 1) || !lua_isfunction(toluaS, 2)) {CCLOG("Wrong Args For RegisterEventHandler!!!!");return 0;}NTSdkBridge::EventType event = (NTSdkBridge::EventType)lua_tointeger(toluaS, 1);LUA_FUNCTION handler = toluafix_ref_function(toluaS, 2, 0);NTSdkBridge::getInstance()->registerEventHandler(event, handler);return 0;
}int lua_ntsdk_getproductamount(lua_State* toluaS) {if (lua_gettop(toluaS) < 1 || !lua_isstring(toluaS, 1)) {CCLOG("Wrong Args For getProductAmount!!!!");return 0;}std::string id = tolua_tocppstring(toluaS, 1, "");float amount = NTSdkBridge::getInstance()->getProductAmount(id);if (amount > 0) {lua_pushnumber(toluaS, amount);return 1;}return 0;
}int lua_ntsdk_getproductcurrency(lua_State* toluaS) {if (lua_gettop(toluaS) < 1 || !lua_isstring(toluaS, 1)) {CCLOG("Wrong Args For getProductAmount!!!!");return 0;}std::string id = tolua_tocppstring(toluaS, 1, "");std::string currency = NTSdkBridge::getInstance()->getProductCurrency(id);lua_pushstring(toluaS, currency.c_str());return 1;
}int lua_ntsdk_getproductdisplayprice(lua_State* toluaS) {if (lua_gettop(toluaS) < 1 || !lua_isstring(toluaS, 1)) {CCLOG("Wrong Args For getProductAmount!!!!");return 0;}std::string id = tolua_tocppstring(toluaS, 1, "");std::string display = NTSdkBridge::getInstance()->getProductDisplayPrice(id);lua_pushstring(toluaS, display.c_str());return 1;
}int lua_ntsdk_eventtrack(lua_State* toluaS) {if (lua_gettop(toluaS) < 1 || !lua_isstring(toluaS, 1)) {CCLOG("Wrong Args For eventTrack");return 0;}std::string event = tolua_tocppstring(toluaS, 1, "");NTSdkBridge::getInstance()->eventTrack(event);return 0;
}static const struct luaL_Reg ntsdk_funcs[] = {{"login", lua_ntsdk_login},{"switchAccount", lua_ntsdk_switchaccount},{"traceAccount", lua_ntsdk_traceaccount},{"bugReport", lua_ntsdk_bugreport},{"userCenter", lua_ntsdk_usercenter},{"storereview", lua_ntsdk_storereview},{"getChannelID", lua_ntsdk_getchannelid},{"queryWithProducts", lua_ntsdk_querywithproducts},{"getProductAmount", lua_ntsdk_getproductamount},{"getProductCurrency", lua_ntsdk_getproductcurrency},{"getProductDisplayPrice", lua_ntsdk_getproductdisplayprice},{"payWithProductID", lua_ntsdk_paywithproductid},{"eventTrack", lua_ntsdk_eventtrack},{"registerEventHandler", lua_ntsdk_registereventhandler},{NULL, NULL},
};int lua_ntsdk_bridge(lua_State* toluaS) {lua_newtable(toluaS);lua_pushvalue(toluaS, -1);lua_setfield(toluaS, LUA_GLOBALSINDEX, "ntsdk");const luaL_Reg* lib = ntsdk_funcs;for (; lib->func; lib++) {lua_pushcfunction(toluaS, lib->func);lua_setfield(toluaS, -2, lib->name);}lua_newtable(toluaS);lua_pushvalue(toluaS, -1);lua_pushinteger(toluaS, (int)NTSdkBridge::EventType::LOGIN);lua_setfield(toluaS, -2, "LOGIN");lua_pushinteger(toluaS, (int)NTSdkBridge::EventType::PAY);lua_setfield(toluaS, -2, "PAY");lua_pop(toluaS, 1);lua_setfield(toluaS, -2, "EventType");lua_pop(toluaS, 1);
}int register_ntsdk_luabinding(lua_State* toluaS) {return lua_ntsdk_bridge(toluaS);
}

lua代码,要判断c++的函数是否存在

为何要判断c++函数是否存在,是因为lua是热更新的,但是c++代码是随着游戏提审applestore用户才会更新,这就要兼容新老版本,如果在lua里调用了老版本没有的c++代码,那就会导致不好的结果,可能直接闪退或者怎样,所以lua代码中必须要判断c++函数是否存在。

local sdk = {}function sdk.getServerID()return "4"
endfunction sdk.login()NTSdk.login()
endfunction sdk.logout()NTSdk.logout()
endfunction sdk.switchAccount()NTSdk.switchAccount()
endfunction sdk.traceAccount(serverid, level)NTSdk.traceAccount(serverid, level)
endfunction sdk.bugReport(serverid, nickname, clientversion)NTSdk.bugReport(serverid, nickname, clientversion)
endfunction sdk.userCenter(serverid, nickname, clientversion)NTSdk.userCenter(serverid, nickname, clientversion)
endfunction sdk.storeReview()NTSdk.storereview()
endfunction sdk.getChannelID()return NTSdk.getChannelID()
endfunction sdk.eventTrack(event)NTSdk.eventTrack(event)
endfunction sdk.queryWithProducts(products)NTSdk.queryWithProducts(products)
endfunction sdk.getProductDisplayPrice(id)return NTSdk.getProductDisplayPrice(id)
endfunction sdk.payWithProductID(productid, serverid, ext, level)NTSdk.payWithProductID(productid, serverid, ext, level)
endfunction sdk.registerLoginListener(handler)NTSdk.registerEventHandler(NTSdk.EventType.LOGIN, handler)
endfunction sdk.registerPayListener(handler)NTSdk.registerEventHandler(NTSdk.EventType.PAY, handler)
endreturn sdk

至此,所有工作结束,这样桥接文件就搞好了,lua可以直接调用c++函数,c++可以直接调用oc函数。

iOS端手游和c++交互 lua和c++交互相关推荐

  1. 刀锋战记2服务端手游开服架设服务器搭建需要什么

    刀锋战记2服务端手游开服架设服务器搭建需要什么 <刀锋战记2>  是一款以魔幻世界为题材精心打造的韩派角色扮演策略战斗类手游,游戏以魔幻世界为背景,采用虚幻4引擎打造,全面塑造的一个虚拟架 ...

  2. 神武服务端手游开服架设服务器搭建教程linux

    神武服务端手游开服架设服务器搭建教程linux 大家好我是艾西,今天跟大家分享下怎么架设神武手游服务端 <神武>回合制网络游戏以西游记为背景,辅以多样化的门派设置.画面.活动玩法和各种创新 ...

  3. 神武3手游微信月礼包服务器,千万红包雨豪礼送不停 《神武3》端手游今日同步上线...

    <神武3>双端今日(11月24日)同步上线,公测盛典活动全面开启!新服齐开,还有海量礼包,千万红包雨以及神兽.iPAD等各种大礼等你来拿! <神武3>今日公测 公测新服齐开 千 ...

  4. 火影忍者android转ios,火影忍者手游iOS及安卓更新内容拆包教程

    导 读 火影忍者手游iOS及安卓更新内容拆包教程,通过教程大家可以在游戏更新加载文件之后进行查询,提前得知游戏即将更新的一系列内容,下面是详细内容. 安卓 先把下载的安装包(2017_com.tenc ...

  5. 火影忍者android转ios,火影忍者手游:iOS和安卓实现大部分互通,但决斗场仍然是个痛!...

    原标题:火影忍者手游:iOS和安卓实现大部分互通,但决斗场仍然是个痛! 目前火影忍者手游iOS用户和安卓用户在很多地方都是可以互通的,比如修罗副本.秘境,但是在决斗场上却是仍然没有互通,至少是目前还没 ...

  6. IOS Replaykit 手游录屏Demo

    http://www.cnblogs.com/huangzizhu/p/5073389.html 1 iOS ReplayKit 录屏SDK 说明 (按照苹果官方的说法是App端加入这些苹果的新特性新 ...

  7. 梦幻诛仙linux纯端架设教程,【梦幻诛仙】【双端手游】【最完美,最全的,视频架设教程】...

    安装宝塔 yum install -y wget && wget -O install.sh http://download.bt.cn/install/install.sh & ...

  8. 梦幻诛仙11职业linux架设手游,梦幻诛仙服务端手游梦诛11职业修真版+搭建教程视频...

    腾讯云 学生机 搭建 11职业梦诛修真版 CentOS 6.8 64位 安装宝塔 yum install -y wget && wget -O install.sh http://do ...

  9. iOS修改手游服务器数据,IOS修改教程

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 再往下面翻 {"m_iType":2,"m_GroupsNodwList":[ {"m_iValue&q ...

最新文章

  1. 牛客练习赛61 D 最短路变短了(最短路,反向最短路)难度⭐⭐⭐★
  2. lisp 线性标注自动避让_泰州支重轮双头车自动化生产线
  3. 25元、264KB内存的微处理器,树莓派官方出品,自带快速休眠模式
  4. mysql删除重复sql_mysql中删除完全重复数据的准确SQL语句
  5. 使用 SQL Server 2000 索引视图提高性能1
  6. DSA签名算法 - Java加密与安全
  7. java获取mavenlib路径,maven install报错致命错误: 在类路径或引导类路径中找不到程序包 java.lang | ZPY博客...
  8. linux的shell脚本if语句,Shell脚本编程之判断语句
  9. C++安全方向(三):3.6 SHA2原理分析和比特币挖矿,以及代码演示
  10. 第二章节 ASP.NET 验证控件(一)
  11. C#.NET 大型企业信息化系统集成快速开发平台 4.2 版本 - 所有的基础数据都可以恢复删除...
  12. 阿里云CDN缓存总结
  13. js ajax通用方法,目前5个流行的AJAX调用JavaScript库
  14. cf914F. Substrings in a String(bitset 字符串匹配)
  15. Android URL Scheme
  16. sata接口 图解 定义_SATA定义及接口
  17. Docker部署MySQL监控工具Lepus
  18. 【Linux】ssh的安装及配置
  19. 网页游戏外挂分析及防范
  20. 利用Tensorflow构建RNN并对序列数据进行建模

热门文章

  1. 离散数学-3 命题逻辑的推理理论
  2. 这3个BT下载工具,可替代迅雷,总有一个适合你
  3. 员工管理系统(服务器和客户端)
  4. 基于OpenXR,Collabora推开源VI-SLAM AR/VR定位系统
  5. JAVA中设置drwxr_x___权限_linux命令中chmod 777 以及drwxr-xr-x分别代表什么意思
  6. windows 下如何让一个窗口置顶?
  7. luogu P3975 [TJOI2015]弦论 SAM
  8. 高仿2017手机QQ
  9. 【标准】视频显示分辨率格式大全
  10. 详解SSD可靠性影响因素、原理和解决方法