/**************************************************************************** Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. http://www.cocos.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated engine source code (the "Software"), a limited, worldwide, royalty-free, non-assignable, revocable and non-exclusive license to use Cocos Creator solely to develop games on your target platforms. You shall not use Cocos Creator software for developing other software or tools that's used for developing games. You are not granted to publish, distribute, sublicense, and/or sell copies of Cocos Creator. The software or tools in this License Agreement are licensed, not sold. Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ****************************************************************************/ // // jsb_node.cpp // cocos2d_js_bindings // // Created by James Chen on 4/26/17. // // #include "jsb_node.hpp" #include "jsb_global.h" #include "jsb_conversions.hpp" #include "ScriptingCore.h" #include "cocos2d.h" using namespace cocos2d; #define STANDALONE_TEST 0 extern se::Object* __jsb_cocos2d_Node_proto; extern se::Class* __jsb_cocos2d__Node_class; extern se::Object* __jsb_cocos2d_Scheduler_proto; se::Object* __jsb_Node_proto = nullptr; se::Class* __jsb_Node_class = nullptr; static bool Node_finalized(se::State& s) { if (s.nativeThisObject()) { Node* thiz = (Node*) s.nativeThisObject(); SE_LOGD("Node_finalized %p ...\n", thiz->getUserData()); CC_SAFE_RELEASE(thiz); } return true; } SE_BIND_FINALIZE_FUNC(Node_finalized) static bool Node_constructor(se::State& s) { SE_LOGD("Node_constructor ...\n"); Node* obj = new Node(); s.thisObject()->setPrivateData(obj); return true; } SE_BIND_CTOR(Node_constructor, __jsb_Node_class, Node_finalized) static bool Node_ctor(se::State& s) { SE_LOGD("Node_ctor ...\n"); Node* obj = new Node(); s.thisObject()->setPrivateData(obj); return true; } SE_BIND_SUB_CLS_CTOR(Node_ctor, __jsb_Node_class, Node_finalized) static bool Node_create(se::State& s) { Node* node = Node::create(); node->retain(); auto obj = se::Object::createObjectWithClass(__jsb_Node_class); obj->setPrivateData(node); s.rval().setObject(obj); return true; } SE_BIND_FUNC(Node_create) static bool Node_onEnter(se::State& s) { ScriptingCore::getInstance()->setCalledFromScript(true); Node* thiz = (Node*)s.nativeThisObject(); thiz->onEnter(); return true; } SE_BIND_FUNC(Node_onEnter) static bool Node_onExit(se::State& s) { ScriptingCore::getInstance()->setCalledFromScript(true); Node* thiz = (Node*)s.nativeThisObject(); thiz->onExit(); return true; } SE_BIND_FUNC(Node_onExit) static bool Node_onEnterTransitionDidFinish(se::State& s) { ScriptingCore::getInstance()->setCalledFromScript(true); Node* thiz = (Node*)s.nativeThisObject(); thiz->onEnterTransitionDidFinish(); return true; } SE_BIND_FUNC(Node_onEnterTransitionDidFinish) static bool Node_onExitTransitionDidStart(se::State& s) { ScriptingCore::getInstance()->setCalledFromScript(true); Node* thiz = (Node*)s.nativeThisObject(); thiz->onExitTransitionDidStart(); return true; } SE_BIND_FUNC(Node_onExitTransitionDidStart) static bool Node_cleanup(se::State& s) { ScriptingCore::getInstance()->setCalledFromScript(true); Node* thiz = (Node*)s.nativeThisObject(); thiz->cleanup(); return true; } SE_BIND_FUNC(Node_cleanup) static bool Node_addChild(se::State& s) { const auto& args = s.args(); Node* thiz = (Node*)s.nativeThisObject(); Node* child = (Node*)args[0].toObject()->getPrivateData(); thiz->addChild(child); return true; } SE_BIND_FUNC(Node_addChild) class ScheduleElement { public: ScheduleElement(se::Object* target, se::Object* func, const std::string& key, uint32_t targetId, uint32_t funcId) : _target(target) , _func(func) , _key(key) , _targetId(targetId) , _funcId(funcId) {} ScheduleElement(ScheduleElement&& o) { *this = std::move(o); } ScheduleElement* operator=(ScheduleElement&& o) { if (this != &o) { _target = o._target; _func = o._func; _key = std::move(o._key); _targetId = o._targetId; _funcId = o._funcId; } return this; } inline se::Object* getTarget() const { return _target; } inline se::Object* getFunc() const { return _func; } inline const std::string& getKey() const { return _key; } inline uint32_t getTargetId() const { return _targetId; } inline uint32_t getFuncId() const { return _funcId; } private: ScheduleElement(const ScheduleElement& o) { assert(false); } ScheduleElement* operator=(const ScheduleElement& o) { assert(false); return this; } se::Object* _target; se::Object* _func; std::string _key; uint32_t _targetId; uint32_t _funcId; }; static uint32_t __scheduleTargetIdCounter = 0; static uint32_t __scheduleFuncIdCounter = 0; static const char* SCHEDULE_TARGET_ID_KEY = "__seScheTargetId"; static const char* SCHEDULE_FUNC_ID_KEY = "__seScheFuncId"; static std::unordered_map> __js_target_schedulekey_map; static std::unordered_map> __js_target_schedule_update_map; static bool isScheduleExist(uint32_t jsFuncId, uint32_t jsTargetId, const ScheduleElement** outElement) { bool found = false; for (const auto& e : __js_target_schedulekey_map) { if (e.first == jsTargetId) { for (const auto& e2 : e.second) { if (e2.first == jsFuncId) { *outElement = &e2.second; found = true; break; } } } if (found) { break; } } if (!found) { *outElement = nullptr; } return found; } static bool isScheduleExist(const std::string& key, uint32_t jsTargetId, const ScheduleElement** outElement) { bool found = false; for (const auto& e : __js_target_schedulekey_map) { if (e.first == jsTargetId) { for (const auto& e2 : e.second) { if (e2.second.getKey() == key) { *outElement = &e2.second; found = true; break; } } } if (found) break; } if (!found) { *outElement = nullptr; } return found; } static void removeSchedule(uint32_t jsFuncId, uint32_t jsTargetId, bool needDetachChild) { auto funcObjKeyMapIter = __js_target_schedulekey_map.find(jsTargetId); if (funcObjKeyMapIter != __js_target_schedulekey_map.end()) { auto& funcMap = funcObjKeyMapIter->second; auto iter = funcMap.find(jsFuncId); if (iter != funcMap.end()) { se::Object* target = iter->second.getTarget(); se::Object* func = iter->second.getFunc(); if (needDetachChild) { target->detachObject(func); } func->decRef(); target->decRef(); funcMap.erase(iter); } if (funcMap.empty()) { __js_target_schedulekey_map.erase(funcObjKeyMapIter); } } } static void removeScheduleForThis(uint32_t jsTargetId, bool needDetachChild) { auto funcObjKeyMapIter = __js_target_schedulekey_map.find(jsTargetId); if (funcObjKeyMapIter != __js_target_schedulekey_map.end()) { auto& funcMap = funcObjKeyMapIter->second; se::Object* target = nullptr; se::Object* func = nullptr; for (auto& e : funcMap) { target = e.second.getTarget(); func = e.second.getFunc(); if (needDetachChild) { target->detachObject(func); } func->decRef(); // Release jsFunc target->decRef(); // Release jsThis } funcMap.clear(); __js_target_schedulekey_map.erase(funcObjKeyMapIter); } } static void removeAllSchedules(bool needDetachChild) { CCLOG("Begin unschedule all callbacks"); for (auto& e1 :__js_target_schedulekey_map) { auto& funcMap = e1.second; CCLOG(">> Found funcMap: %d", (int)funcMap.size()); se::Object* target = nullptr; se::Object* func = nullptr; for (auto& e : funcMap) { target = e.second.getTarget(); func = e.second.getFunc(); if (needDetachChild) { CCLOG("detachObject: owner: %p, target: %p", target, func); target->detachObject(func); } target->decRef(); // Release jsThis func->decRef(); // Release jsFunc } funcMap.clear(); } __js_target_schedulekey_map.clear(); } static void removeAllScheduleUpdates() { for (auto& e2 : __js_target_schedule_update_map) { e2.second.second->decRef(); } __js_target_schedule_update_map.clear(); } static void removeAllScheduleAndUpdate(bool needDetachChild) { removeAllSchedules(needDetachChild); removeAllScheduleUpdates(); } static bool isScheduleUpdateExist(uint32_t targetId) { for (const auto& e : __js_target_schedule_update_map) { if (e.first == targetId) { return true; } } return false; } static void removeScheduleUpdate(uint32_t targetId) { auto iter = __js_target_schedule_update_map.find(targetId); if (iter != __js_target_schedule_update_map.end()) { iter->second.second->decRef(); __js_target_schedule_update_map.erase(iter); } } static void removeScheduleUpdatesForMinPriority(int minPriority) { int foundPriority = 0; auto iter = __js_target_schedule_update_map.begin(); while (iter != __js_target_schedule_update_map.end()) { foundPriority = iter->second.first; if (foundPriority >= minPriority) { iter->second.second->decRef(); iter = __js_target_schedule_update_map.erase(iter); } else { ++iter; } } } static void insertScheduleUpdate(uint32_t targetId, int priority, se::Object* targetObj) { assert(__js_target_schedule_update_map.find(targetId) == __js_target_schedule_update_map.end()); __js_target_schedule_update_map[targetId] = std::make_pair(priority, targetObj); targetObj->incRef(); } static void insertSchedule(uint32_t funcId, uint32_t targetId, ScheduleElement&& element) { auto& funcKeyMap = __js_target_schedulekey_map[targetId]; assert(funcKeyMap.find(funcId) == funcKeyMap.end()); element.getTarget()->incRef(); element.getFunc()->incRef(); funcKeyMap.emplace(funcId, std::move(element)); } static bool isTargetExistInScheduler(uint32_t targetId) { assert(targetId != 0); // Iterating the schedule func map for (const auto& e : __js_target_schedulekey_map) { if (e.first == targetId) { return true; } } // Iterating the schedule update map for (const auto& e : __js_target_schedule_update_map) { if (e.first == targetId) { return true; } } return false; } class UnscheduleNotifier { public: UnscheduleNotifier(uint32_t funcId, uint32_t targetId) : _funcId(funcId) , _targetId(targetId) { } ~UnscheduleNotifier() { // SE_LOGD("~UnscheduleNotifier, targetId: %u, funcId: %u\n", _targetId, _funcId); se::ScriptEngine::getInstance()->clearException(); se::AutoHandleScope hs; removeSchedule(_funcId, _targetId, false); } private: uint32_t _funcId; uint32_t _targetId; }; static uint32_t __idx = 0; static bool Scheduler_scheduleCommon(Scheduler* scheduler, const se::Value& jsThis, const se::Value& jsFunc, float interval, unsigned int repeat, float delay, bool isPaused, bool toRootTarget, const std::string& callFromDebug) { assert(jsThis.isObject()); assert(jsFunc.isObject()); assert(jsFunc.toObject()->isFunction()); jsThis.toObject()->attachObject(jsFunc.toObject()); std::string key; se::Value targetIdVal; se::Value funcIdVal; uint32_t targetId = 0; uint32_t funcId = 0; if (jsThis.toObject()->getProperty(SCHEDULE_TARGET_ID_KEY, &targetIdVal) && targetIdVal.isNumber()) { targetId = targetIdVal.toUint32(); } if (jsFunc.toObject()->getProperty(SCHEDULE_FUNC_ID_KEY, &funcIdVal) && funcIdVal.isNumber()) { funcId = funcIdVal.toUint32(); } if (targetIdVal.isNumber() && funcIdVal.isNumber()) { const ScheduleElement* scheduleElem = nullptr; bool found = isScheduleExist(funcId, targetId, &scheduleElem); if (found) key = scheduleElem->getKey(); if (found && !key.empty()) { removeSchedule(funcId, targetId, true); scheduler->unschedule(key, reinterpret_cast(targetId)); } } else { if (targetId == 0) { targetId = ++__scheduleTargetIdCounter; // counter is probably overfollow, it maybe 0 which is invalid id. Increase 1. if (targetId == 0) { targetId = ++__scheduleTargetIdCounter; } jsThis.toObject()->setProperty(SCHEDULE_TARGET_ID_KEY, se::Value(targetId)); } if (funcId == 0) { funcId = ++__scheduleFuncIdCounter; // counter is probably overfollow, it maybe 0 which is invalid id. Increase 1. if (funcId == 0) { funcId = ++__scheduleFuncIdCounter; } jsFunc.toObject()->setProperty(SCHEDULE_FUNC_ID_KEY, se::Value(funcId)); } } key = StringUtils::format("__node_schedule_key:%u", __idx++); se::Object* target = jsThis.toObject(); insertSchedule(funcId, targetId, ScheduleElement(target, jsFunc.toObject(), key, targetId, funcId)); std::shared_ptr unscheduleNotifier = std::make_shared(funcId, targetId); if (toRootTarget) { target->root(); } scheduler->schedule([jsThis, jsFunc, unscheduleNotifier, callFromDebug](float dt){ se::ScriptEngine::getInstance()->clearException(); se::AutoHandleScope hs; se::Object* thisObj = jsThis.toObject(); se::Object* funcObj = jsFunc.toObject(); se::ValueArray args; args.push_back(se::Value((double)dt)); bool ok = funcObj->call(args, thisObj); if (!ok) { CCLOGERROR("Invoking schedule callback failed, where: %s", callFromDebug.c_str()); } }, reinterpret_cast(targetId), interval, repeat, delay, isPaused, key); return true; } static bool Node_schedule(se::State& s) { const auto& args = s.args(); int argc = (int)args.size(); #if 0//COCOS2D_DEBUG > 0 SE_LOGD("--------------------------\nschedule target count: %d\n", (int)__js_target_schedulekey_map.size()); int totalCount = 0; for (const auto& e1 : __js_target_schedulekey_map) { SE_LOGD("schedule target: %p, functions: %d\n", e1.first, (int)e1.second.size()); totalCount += (int)e1.second.size(); } SE_LOGD("total: %d-------------------------- \n", totalCount); #endif if (argc >= 1) { Node* thiz = (Node*)s.nativeThisObject(); se::Value jsThis(s.thisObject()); se::Value jsFunc(args[0]); float interval = 0.0f; unsigned int repeat = CC_REPEAT_FOREVER; float delay = 0.0f; bool ok = false; if (argc >= 2) { ok = seval_to_float(args[1], &interval); SE_PRECONDITION2(ok, false, "Converting 'interval' argument failed"); } if (argc >= 3) { ok = seval_to_uint32(args[2], &repeat); SE_PRECONDITION2(ok, false, "Converting 'interval' argument failed"); } if (argc >= 4) { ok = seval_to_float(args[3], &delay); SE_PRECONDITION2(ok, false, "Converting 'delay' argument failed"); } return Scheduler_scheduleCommon(thiz->getScheduler(), jsThis, jsFunc, interval, repeat, delay, !thiz->isRunning(), false, "cc.Node.schedule"); } SE_REPORT_ERROR("wrong number of arguments: %d, expected: %s", argc, ">=1"); return false; } SE_BIND_FUNC(Node_schedule) static bool Node_scheduleOnce(se::State& s) { const auto& args = s.args(); size_t argc = args.size(); #if 0//COCOS2D_DEBUG > 0 SE_LOGD("--------------------------\nschedule target count: %d\n", (int)__js_target_schedulekey_map.size()); for (const auto& e1 : __js_target_schedulekey_map) { SE_LOGD("schedule target: %p, functions: %d\n", e1.first, (int)e1.second.size()); } SE_LOGD("-------------------------- \n"); #endif Node* thiz = (Node*)s.nativeThisObject(); se::Value jsThis(s.thisObject()); se::Value jsFunc(args[0]); float delay = 0.0f; bool ok = false; if (argc >= 2) { ok = seval_to_float(args[1], &delay); SE_PRECONDITION2(ok, false, "Converting 'delay' argument failed"); } return Scheduler_scheduleCommon(thiz->getScheduler(), jsThis, jsFunc, 0.0f, 0, delay, !thiz->isRunning(), false, "cc.Node.scheduleOnce"); } SE_BIND_FUNC(Node_scheduleOnce) class UnscheduleUpdateNotifier { public: UnscheduleUpdateNotifier(uint32_t targetId) : _targetId(targetId) { } ~UnscheduleUpdateNotifier() { // SE_LOGD("~UnscheduleUpdateNotifier: %p\n", _target); removeScheduleUpdate(_targetId); } private: uint32_t _targetId; }; static bool Scheduler_unscheduleUpdateCommon(Scheduler* scheduler, se::Object* jsTarget, uint32_t* foundTargetId = nullptr) { assert(jsTarget != nullptr); se::Value targetIdVal; if (!jsTarget->getProperty(SCHEDULE_TARGET_ID_KEY, &targetIdVal) || !targetIdVal.isNumber()) { if (foundTargetId != nullptr) *foundTargetId = 0; return false; } uint32_t targetId = targetIdVal.toUint32(); if (foundTargetId != nullptr) *foundTargetId = targetId; bool found = isScheduleUpdateExist(targetId); if (found) { removeScheduleUpdate(targetId); scheduler->unscheduleUpdate(reinterpret_cast(targetId)); } return found; } static bool Scheduler_scheduleUpdateCommon(Scheduler* scheduler, const se::Value& jsThis, int priority, bool isPaused) { se::Object* jsTargetObj = jsThis.toObject(); uint32_t targetId = 0; Scheduler_unscheduleUpdateCommon(scheduler, jsTargetObj, &targetId); if (targetId == 0) { targetId = ++__scheduleTargetIdCounter; // counter is probably overfollow, it maybe 0 which is invalid id. Increase 1. if (targetId == 0) { targetId = ++__scheduleTargetIdCounter; } jsTargetObj->setProperty(SCHEDULE_TARGET_ID_KEY, se::Value(targetId)); } insertScheduleUpdate(targetId, priority, jsTargetObj); std::shared_ptr scheduleUpdateWrapper = std::make_shared(targetId); se::Value thisVal = jsThis; scheduler->schedulePerFrame([thisVal, scheduleUpdateWrapper](float dt){ se::ScriptEngine::getInstance()->clearException(); se::AutoHandleScope hs; se::Value funcVal; if (thisVal.toObject()->getProperty("update", &funcVal) && funcVal.isObject() && funcVal.toObject()->isFunction()) { se::ValueArray args; args.reserve(1); args.push_back(se::Value(dt)); funcVal.toObject()->call(args, thisVal.toObject()); } }, reinterpret_cast(targetId), priority, isPaused); return true; } static bool Node_scheduleUpdate(se::State& s) { #if COCOS2D_DEBUG > 1 SE_LOGD("--------------------------\nscheduleUpdate target count: %d\n", (int)__js_target_schedule_update_map.size()); for (const auto& e1 : __js_target_schedule_update_map) { SE_LOGD("target: %u, updated: priority: %d\n", e1.first, e1.second.first); } SE_LOGD("-------------------------- \n"); #endif Node* thiz = (Node*)s.nativeThisObject(); se::Value jsThis(s.thisObject()); return Scheduler_scheduleUpdateCommon(thiz->getScheduler(), jsThis, 0, !thiz->isRunning()); } SE_BIND_FUNC(Node_scheduleUpdate) static bool Node_scheduleUpdateWithPriority(se::State& s) { const auto& args = s.args(); int argc = (int)args.size(); #if COCOS2D_DEBUG > 1 SE_LOGD("--------------------------\nscheduleUpdate target count: %d\n", (int)__js_target_schedule_update_map.size()); for (const auto& e1 : __js_target_schedule_update_map) { SE_LOGD("target: %u, updated: priority: %d\n", e1.first, e1.second.first); } SE_LOGD("-------------------------- \n"); #endif Node* thiz = (Node*)s.nativeThisObject(); se::Value jsThis(s.thisObject()); int priority = 0; if (argc == 1) { bool ok = seval_to_int32(args[0], &priority); SE_PRECONDITION2(ok, false, "Converting priority failed!"); return Scheduler_scheduleUpdateCommon(thiz->getScheduler(), jsThis, priority, !thiz->isRunning()); } SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", argc, 1); return false; } SE_BIND_FUNC(Node_scheduleUpdateWithPriority) static bool Node_unscheduleUpdate(se::State& s) { int argc = (int)s.args().size(); if (argc == 0) { Node* node = (Node*)s.nativeThisObject(); Scheduler_unscheduleUpdateCommon(node->getScheduler(), s.thisObject()); return true; } SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", argc, 0); return false; } SE_BIND_FUNC(Node_unscheduleUpdate) static bool Scheduler_unscheduleCommon(Scheduler* scheduler, const se::Value& jsThis, const se::Value& jsFuncOrKey) { std::string key; bool found = false; se::Value targetIdVal; se::Value funcIdVal; jsThis.toObject()->getProperty(SCHEDULE_TARGET_ID_KEY, &targetIdVal); if (!targetIdVal.isNumber()) return true; uint32_t targetId = targetIdVal.toUint32(); uint32_t funcId = 0; if (jsFuncOrKey.isString() || jsFuncOrKey.isNumber()) { key = jsFuncOrKey.toStringForce(); const ScheduleElement* scheduleElem = nullptr; found = isScheduleExist(key, targetId, &scheduleElem); if (found) funcId = scheduleElem->getFuncId(); } else if (jsFuncOrKey.isObject()) { if (jsFuncOrKey.toObject()->getProperty(SCHEDULE_FUNC_ID_KEY, &funcIdVal) && funcIdVal.isNumber()) { funcId = funcIdVal.toUint32(); const ScheduleElement* scheduleElem = nullptr; found = isScheduleExist(funcId, targetId, &scheduleElem); if (found) key = scheduleElem->getKey(); } } else { assert(false); } if (!targetIdVal.isNumber() || !funcIdVal.isNumber()) { return true; } if (found && !key.empty()) { removeSchedule(funcId, targetId, true); scheduler->unschedule(key, reinterpret_cast(targetId)); } else { SE_LOGD("WARNING: %s not found\n", __FUNCTION__); } return true; } static bool Node_unschedule(se::State& s) { const auto& args = s.args(); int argc = (int)args.size(); if (argc == 1) { Node* thiz = (Node*)s.nativeThisObject(); se::Value jsThis(s.thisObject()); se::Value jsFunc(args[0]); return Scheduler_unscheduleCommon(thiz->getScheduler(), jsThis, jsFunc); } SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", argc, 1); return false; } SE_BIND_FUNC(Node_unschedule) static bool Scheduler_isScheduled(Scheduler* scheduler, const se::Value& jsThis, const se::Value& jsFuncOrKey) { se::Value targetIdVal; if (!jsThis.toObject()->getProperty(SCHEDULE_TARGET_ID_KEY, &targetIdVal) || !targetIdVal.isNumber()) return false; uint32_t targetId = targetIdVal.toUint32(); if (isTargetExistInScheduler(targetId)) { if (jsFuncOrKey.isString() || jsFuncOrKey.isNumber()) { return scheduler->isScheduled(jsFuncOrKey.toStringForce(), reinterpret_cast(targetId)); } else if (jsFuncOrKey.isObject()) { std::string key; se::Value funcIdVal; if (jsFuncOrKey.toObject()->getProperty(SCHEDULE_FUNC_ID_KEY, &funcIdVal) && funcIdVal.isNumber()) { uint32_t funcId = funcIdVal.toUint32(); const ScheduleElement* scheduleElem = nullptr; if (isScheduleExist(funcId, targetId, &scheduleElem)) { key = scheduleElem->getKey(); if (!key.empty()) { return scheduler->isScheduled(key, reinterpret_cast(targetId)); } } } } else { assert(false); } } return false; } static bool Node_isScheduled(se::State& s) { const auto& args = s.args(); int argc = (int)args.size(); if (argc == 1) { Node* thiz = (Node*)s.nativeThisObject(); se::Value jsThis(s.thisObject()); se::Value jsFuncOrKey(args[0]); bool isScheduled = Scheduler_isScheduled(thiz->getScheduler(), jsThis, jsFuncOrKey); s.rval().setBoolean(isScheduled); return true; } SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", argc, 1); return false; } SE_BIND_FUNC(Node_isScheduled) static bool Scheduler_unscheduleAllCallbacksCommon(Scheduler* scheduler, se::Object* jsThis, bool needDetachChild) { se::Value targetIdVal; if (!jsThis->getProperty(SCHEDULE_TARGET_ID_KEY, &targetIdVal) || !targetIdVal.isNumber()) return true; uint32_t targetId = targetIdVal.toUint32(); if (isTargetExistInScheduler(targetId)) { removeScheduleForThis(targetId, needDetachChild); removeScheduleUpdate(targetId); scheduler->unscheduleAllForTarget(reinterpret_cast(targetId)); } return true; } static bool Node_unscheduleAllCallbacks(se::State& s) { const auto& args = s.args(); int argc = (int)args.size(); Node* thiz = (Node*)s.nativeThisObject(); if (argc == 0) { return Scheduler_unscheduleAllCallbacksCommon(thiz->getScheduler(), s.thisObject(), true); } SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", argc, 0); return false; } SE_BIND_FUNC(Node_unscheduleAllCallbacks) static bool Node_getChildren(se::State& s) { Node* thiz = (Node*)s.nativeThisObject(); const auto& children = thiz->getChildren(); bool ok = Vector_to_seval(children, &s.rval()); return ok; } SE_BIND_FUNC(Node_getChildren) static bool Node_foo(se::State& s) { s.rval().setString("hello world"); return true; } SE_BIND_FUNC(Node_foo) static bool Node_set_x(se::State& s) { const auto& args = s.args(); Node* thiz = (Node*)s.nativeThisObject(); float x = args[0].toNumber(); SE_LOGD("cc.Node set_x (%f) native obj: %p\n", x, thiz); thiz->setPositionX(x); return true; } SE_BIND_PROP_SET(Node_set_x) static bool Node_get_x(se::State& s) { Node* thiz = (Node*)s.nativeThisObject(); s.rval().setFloat(thiz->getPositionX()); return true; } SE_BIND_PROP_GET(Node_get_x) static bool Node_set_y(se::State& s) { const auto& args = s.args(); Node* thiz = (Node*)s.nativeThisObject(); float y = args[0].toNumber(); SE_LOGD("cc.Node set_y (%f) native obj: %p\n", y, thiz); thiz->setPositionY(y); return true; } SE_BIND_PROP_SET(Node_set_y) static bool Node_get_y(se::State& s) { Node* thiz = (Node*)s.nativeThisObject(); s.rval().setFloat(thiz->getPositionY()); return true; } SE_BIND_PROP_GET(Node_get_y) static bool Node_setContentSize(se::State& s) { const auto& args = s.args(); size_t argc = args.size(); Node* cobj = (Node*)s.nativeThisObject(); bool ok = true; if (argc == 1) { cocos2d::Size arg0; ok &= seval_to_Size(args[0], &arg0); SE_PRECONDITION2(ok, false, "Error processing arguments"); cobj->setContentSize(arg0); return true; } else if (argc == 2) { float width = 0.0f; float height = 0.0f; ok &= seval_to_float(args[0], &width); ok &= seval_to_float(args[1], &height); cobj->setContentSize(cocos2d::Size(width, height)); return true; } SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", (int)argc, 2); return false; } SE_BIND_FUNC(Node_setContentSize) static bool Node_setAnchorPoint(se::State& s) { const auto& args = s.args(); size_t argc = args.size(); Node* cobj = (Node*)s.nativeThisObject(); bool ok = true; if (argc == 1) { cocos2d::Vec2 arg0; ok &= seval_to_Vec2(args[0], &arg0); SE_PRECONDITION2(ok, false, "Error processing arguments"); cobj->setAnchorPoint(arg0); return true; } else if (argc == 2) { float x = 0.0f; float y = 0.0f; ok &= seval_to_float(args[0], &x); ok &= seval_to_float(args[1], &y); cobj->setAnchorPoint(cocos2d::Vec2(x, y)); return true; } SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", (int)argc, 2); return false; } SE_BIND_FUNC(Node_setAnchorPoint) static bool Node_setPosition(se::State& s) { const auto& args = s.args(); size_t argc = args.size(); Node* cobj = (Node*)s.nativeThisObject(); bool ok = true; if (argc == 1) { cocos2d::Vec2 arg0; ok &= seval_to_Vec2(args[0], &arg0); SE_PRECONDITION2(ok, false, "Error processing arguments"); cobj->setPosition(arg0); return true; } else if (argc == 2) { float x = 0.0f; float y = 0.0f; ok &= seval_to_float(args[0], &x); ok &= seval_to_float(args[1], &y); cobj->setPosition(x, y); return true; } SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", (int)argc, 2); return false; } SE_BIND_FUNC(Node_setPosition) // Scheduler static bool js_cocos2dx_Scheduler_scheduleUpdateForTarget(se::State& s) { const auto& args = s.args(); int argc = (int)args.size(); if (argc >= 1) { bool ok = true; se::Value jsTarget = args[0]; int priority = 0; bool isPaused = false; if (argc >= 2) { ok = seval_to_int32(args[1], &priority); SE_PRECONDITION2(ok, false, "Error processing arguments"); } if (argc >= 3) { ok = seval_to_boolean(args[2], &isPaused); SE_PRECONDITION2(ok, false, "Error processing arguments"); } Scheduler* cobj = (Scheduler*)s.nativeThisObject(); Scheduler_scheduleUpdateCommon(cobj, jsTarget, priority, isPaused); return true; } SE_REPORT_ERROR("wrong number of arguments: %d, expected: %s", argc, ">=1"); return false; } SE_BIND_FUNC(js_cocos2dx_Scheduler_scheduleUpdateForTarget) static bool js_cocos2dx_Scheduler_unscheduleUpdate(se::State& s) { const auto& args = s.args(); int argc = (int)args.size(); if (argc >= 1) { Scheduler* cobj = (Scheduler*)s.nativeThisObject(); se::Value jsTarget = args[0]; Scheduler_unscheduleUpdateCommon(cobj, jsTarget.toObject()); return true; } SE_REPORT_ERROR("wrong number of arguments: %d, expected: %s", argc, "1"); return false; } SE_BIND_FUNC(js_cocos2dx_Scheduler_unscheduleUpdate) static bool js_cocos2dx_Scheduler_schedule(se::State& s) { const auto& args = s.args(); int argc = (int)args.size(); #if 0//COCOS2D_DEBUG > 0 SE_LOGD("--------------------------\nschedule target count: %d\n", (int)__js_target_schedulekey_map.size()); for (const auto& e1 : __js_target_schedulekey_map) { SE_LOGD("schedule target: %p, functions: %d\n", e1.first, (int)e1.second.size()); } SE_LOGD("-------------------------- \n"); #endif // if (argc >= 4) { Scheduler* cobj = (Scheduler*)s.nativeThisObject(); se::Value jsFunc; se::Value jsThis; if (args[0].isObject() && args[0].toObject()->isFunction()) { jsFunc = args[0]; jsThis = args[1]; } else { jsFunc = args[1]; jsThis = args[0]; } bool isBindedObject = jsThis.toObject()->getPrivateData() != nullptr; // SE_LOGD("%s, is binded object: %s\n", __FUNCTION__, isBindedObject ? "true" : "false"); assert(jsThis.isObject()); assert(!jsThis.toObject()->isFunction()); bool ok = false; float interval = 0.0f; unsigned int repeat = CC_REPEAT_FOREVER; float delay = 0.0f; bool isPaused = false; ok = seval_to_float(args[2], &interval); SE_PRECONDITION2(ok, false, "Converting 'interval' argument failed"); if (argc == 4) { // callback, target, interval, paused ok = seval_to_boolean(args[3], &isPaused); SE_PRECONDITION2(ok, false, "Converting 'isPaused' argument failed"); } else if (argc >= 6) { // callback, target, interval, repeat, delay, paused // repeat ok = seval_to_uint32(args[3], &repeat); SE_PRECONDITION2(ok, false, "Converting 'interval' argument failed"); // delay ok = seval_to_float(args[4], &delay); SE_PRECONDITION2(ok, false, "Converting 'delay' argument failed"); // isPaused ok = seval_to_boolean(args[5], &isPaused); SE_PRECONDITION2(ok, false, "Converting 'isPaused' argument failed"); } return Scheduler_scheduleCommon(cobj, jsThis, jsFunc, interval, repeat, delay, isPaused, !isBindedObject, "cc.Scheduler.schedule"); } SE_REPORT_ERROR("wrong number of arguments: %d, expected: %s", argc, ">=2"); return false; } SE_BIND_FUNC(js_cocos2dx_Scheduler_schedule) static bool js_cocos2dx_Scheduler_unschedule(se::State& s) { const auto& args = s.args(); int argc = (int)args.size(); if (argc >= 2) { se::Value jsFuncOrKey; se::Value jsTarget; if (args[0].isString() || (args[0].isObject() && args[0].toObject()->isFunction())) { jsFuncOrKey = args[0]; jsTarget = args[1]; } else { jsFuncOrKey = args[1]; jsTarget = args[0]; } Scheduler* cobj = (Scheduler*)s.nativeThisObject(); return Scheduler_unscheduleCommon(cobj, jsTarget, jsFuncOrKey); } SE_REPORT_ERROR("wrong number of arguments: %d, expected: %s", argc, ">=2"); return false; } SE_BIND_FUNC(js_cocos2dx_Scheduler_unschedule) static bool js_cocos2dx_Scheduler_unscheduleAllForTarget(se::State& s) { const auto& args = s.args(); int argc = (int)args.size(); Scheduler* cobj = (Scheduler*)s.nativeThisObject(); if (argc == 1) { se::Value target = args[0]; return Scheduler_unscheduleAllCallbacksCommon(cobj, target.toObject(), true); } SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", argc, 1); return false; } SE_BIND_FUNC(js_cocos2dx_Scheduler_unscheduleAllForTarget) static bool js_cocos2dx_Scheduler_unscheduleAllCallbacks(se::State& s) { const auto& args = s.args(); int argc = (int)args.size(); if (argc == 0) { removeAllScheduleAndUpdate(true); Scheduler* cobj = (Scheduler*)s.nativeThisObject(); cobj->unscheduleAll(); CCLOG("After unschedule all callbacks"); return true; } SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", argc, 0); return false; } SE_BIND_FUNC(js_cocos2dx_Scheduler_unscheduleAllCallbacks) static bool js_cocos2dx_Scheduler_unscheduleAllCallbacksWithMinPriority(se::State& s) { const auto& args = s.args(); int argc = (int)args.size(); if (argc == 1) { int minPriority = 0; bool ok = false; ok = seval_to_int32(args[0], &minPriority); SE_PRECONDITION2(ok, false, "Converting minPriority failed!"); removeAllSchedules(true); removeScheduleUpdatesForMinPriority(minPriority); Scheduler* cobj = (Scheduler*)s.nativeThisObject(); cobj->unscheduleAllWithMinPriority(minPriority); return true; } SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", argc, 0); return false; } SE_BIND_FUNC(js_cocos2dx_Scheduler_unscheduleAllCallbacksWithMinPriority) static bool js_cocos2dx_Scheduler_isScheduled(se::State& s) { const auto& args = s.args(); int argc = (int)args.size(); if (argc == 2) { Scheduler* thiz = (Scheduler*)s.nativeThisObject(); se::Value jsFuncOrKey(args[0]); se::Value jsThis(args[1]); bool isScheduled = Scheduler_isScheduled(thiz, jsThis, jsFuncOrKey); s.rval().setBoolean(isScheduled); return true; } SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", argc, 2); return false; } SE_BIND_FUNC(js_cocos2dx_Scheduler_isScheduled) static bool js_cocos2dx_Scheduler_pauseTarget(se::State& s) { const auto& args = s.args(); int argc = (int)args.size(); if (argc == 1) { Scheduler* cobj = (Scheduler*)s.nativeThisObject(); se::Value targetIdVal; if (args[0].toObject()->getProperty(SCHEDULE_TARGET_ID_KEY, &targetIdVal) && targetIdVal.isNumber()) { uint32_t targetId = targetIdVal.toUint32(); if (isTargetExistInScheduler(targetId)) { cobj->pauseTarget(reinterpret_cast(targetId)); } } return true; } SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", argc, 1); return false; } SE_BIND_FUNC(js_cocos2dx_Scheduler_pauseTarget) static bool js_cocos2dx_Scheduler_resumeTarget(se::State& s) { const auto& args = s.args(); int argc = (int)args.size(); if (argc == 1) { Scheduler* cobj = (Scheduler*)s.nativeThisObject(); se::Value targetIdVal; if (args[0].toObject()->getProperty(SCHEDULE_TARGET_ID_KEY, &targetIdVal) && targetIdVal.isNumber()) { uint32_t targetId = targetIdVal.toUint32(); if (isTargetExistInScheduler(targetId)) { cobj->resumeTarget(reinterpret_cast(targetId)); } } return true; } SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", argc, 1); return false; } SE_BIND_FUNC(js_cocos2dx_Scheduler_resumeTarget) static bool js_cocos2dx_Scheduler_isTargetPaused(se::State& s) { const auto& args = s.args(); int argc = (int)args.size(); if (argc == 1) { Scheduler* cobj = (Scheduler*)s.nativeThisObject(); se::Value targetIdVal; if (args[0].toObject()->getProperty(SCHEDULE_TARGET_ID_KEY, &targetIdVal) && targetIdVal.isNumber()) { uint32_t targetId = targetIdVal.toUint32(); if (isTargetExistInScheduler(targetId)) { bool isPaused = cobj->isTargetPaused(reinterpret_cast(targetId)); s.rval().setBoolean(isPaused); return true; } } s.rval().setBoolean(false); return true; } SE_REPORT_ERROR("wrong number of arguments: %d, was expecting %d", argc, 1); return false; } SE_BIND_FUNC(js_cocos2dx_Scheduler_isTargetPaused) static void resumeAllSchedulesForTarget(Node* node, se::Object* jsThis) { node->getScheduler()->resumeTarget(jsThis); } static void pauseAllSchedulesForTarget(Node* node, se::Object* jsThis) { node->getScheduler()->pauseTarget(jsThis); } static void cleanupAllSchedulesForTarget(Node* node, se::Object* jsThis) { //IDEA: ?? Do we need this since we have already had a 'UnscheduleNotifier' and 'UnscheduleUpdateWrapper'. node->getScheduler()->unscheduleAllForTarget(jsThis); } static bool onReceiveNodeEvent(void* node, ScriptingCore::NodeEventType type) { auto iter = se::NativePtrToObjectMap::find(node); if (iter == se::NativePtrToObjectMap::end()) return false; se::ScriptEngine::getInstance()->clearException(); se::AutoHandleScope hs; se::Object* target = iter->second; const char* funcName = nullptr; bool ret = false; #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM JSNative func = nullptr; #endif if (type == ScriptingCore::NodeEventType::ENTER) { funcName = "onEnter"; #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM func = _SE(Node_onEnter); #endif } else if (type == ScriptingCore::NodeEventType::EXIT) { funcName = "onExit"; #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM func = _SE(Node_onExit); #endif } else if (type == ScriptingCore::NodeEventType::ENTER_TRANSITION_DID_FINISH) { funcName = "onEnterTransitionDidFinish"; #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM func = _SE(Node_onEnterTransitionDidFinish); #endif } else if (type == ScriptingCore::NodeEventType::EXIT_TRANSITION_DID_START) { funcName = "onExitTransitionDidStart"; #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM func = _SE(Node_onExitTransitionDidStart); #endif } else if (type == ScriptingCore::NodeEventType::CLEANUP) { funcName = "cleanup"; #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM func = _SE(Node_cleanup); #endif } else { assert(false); } se::Value funcVal; bool ok = target->getProperty(funcName, &funcVal); #if SCRIPT_ENGINE_TYPE == SCRIPT_ENGINE_SM bool isNativeFunc = funcVal.toObject()->_isNativeFunction(func); #else bool isNativeFunc = funcVal.toObject()->_isNativeFunction(); #endif if (ok && !isNativeFunc) { ret = funcVal.toObject()->call(se::EmptyValueArray, target); } // Handle schedule stuff if (type == ScriptingCore::NodeEventType::ENTER) { resumeAllSchedulesForTarget((Node*)node, target); } else if (type == ScriptingCore::NodeEventType::EXIT) { pauseAllSchedulesForTarget((Node*)node, target); } else if (type == ScriptingCore::NodeEventType::CLEANUP) { cleanupAllSchedulesForTarget((Node*)node, target); } return ret; } bool jsb_register_Node_manual(se::Object* global) { #if STANDALONE_TEST se::Object* ccObj = nullptr; getOrCreatePlainObject_r("cc", global, &ccObj); auto cls = se::Class::create("Node", ccObj, nullptr, _SE(Node_constructor)); cls->defineStaticFunction("create", _SE(Node_create)); cls->defineProperty("x", _SE(Node_get_x), _SE(Node_set_x)); cls->defineProperty("y", _SE(Node_get_y), _SE(Node_set_y)); cls->defineFunction("ctor", _SE(Node_ctor)); se::ScriptEngine::getInstance()->addAfterCleanupHook([](){ SAFE_DEC_REF(ccObj); }); #else auto cls = __jsb_cocos2d_Node_proto; #endif cls->defineFunction("onEnter", _SE(Node_onEnter)); cls->defineFunction("onExit", _SE(Node_onExit)); cls->defineFunction("onEnterTransitionDidFinish", _SE(Node_onEnterTransitionDidFinish)); cls->defineFunction("onExitTransitionDidStart", _SE(Node_onExitTransitionDidStart)); cls->defineFunction("cleanup", _SE(Node_cleanup)); cls->defineFunction("schedule", _SE(Node_schedule)); cls->defineFunction("scheduleOnce", _SE(Node_scheduleOnce)); cls->defineFunction("scheduleUpdateWithPriority", _SE(Node_scheduleUpdateWithPriority)); cls->defineFunction("scheduleUpdate", _SE(Node_scheduleUpdate)); cls->defineFunction("unscheduleUpdate", _SE(Node_unscheduleUpdate)); cls->defineFunction("unschedule", _SE(Node_unschedule)); cls->defineFunction("unscheduleAllCallbacks", _SE(Node_unscheduleAllCallbacks)); cls->defineFunction("isScheduled", _SE(Node_isScheduled)); cls->defineFunction("setContentSize", _SE(Node_setContentSize)); cls->defineFunction("setAnchorPoint", _SE(Node_setAnchorPoint)); cls->defineFunction("setPosition", _SE(Node_setPosition)); auto schedulerProto = __jsb_cocos2d_Scheduler_proto; schedulerProto->defineFunction("scheduleUpdateForTarget", _SE(js_cocos2dx_Scheduler_scheduleUpdateForTarget)); schedulerProto->defineFunction("scheduleUpdate", _SE(js_cocos2dx_Scheduler_scheduleUpdateForTarget)); schedulerProto->defineFunction("unscheduleUpdate", _SE(js_cocos2dx_Scheduler_unscheduleUpdate)); schedulerProto->defineFunction("schedule", _SE(js_cocos2dx_Scheduler_schedule)); schedulerProto->defineFunction("scheduleCallbackForTarget", _SE(js_cocos2dx_Scheduler_schedule)); schedulerProto->defineFunction("unschedule", _SE(js_cocos2dx_Scheduler_unschedule)); schedulerProto->defineFunction("unscheduleCallbackForTarget", _SE(js_cocos2dx_Scheduler_unschedule)); schedulerProto->defineFunction("unscheduleAllForTarget", _SE(js_cocos2dx_Scheduler_unscheduleAllForTarget)); schedulerProto->defineFunction("unscheduleAllCallbacks", _SE(js_cocos2dx_Scheduler_unscheduleAllCallbacks)); schedulerProto->defineFunction("unscheduleAllCallbacksWithMinPriority", _SE(js_cocos2dx_Scheduler_unscheduleAllCallbacksWithMinPriority)); schedulerProto->defineFunction("isScheduled", _SE(js_cocos2dx_Scheduler_isScheduled)); schedulerProto->defineFunction("pauseTarget", _SE(js_cocos2dx_Scheduler_pauseTarget)); schedulerProto->defineFunction("resumeTarget", _SE(js_cocos2dx_Scheduler_resumeTarget)); schedulerProto->defineFunction("isTargetPaused", _SE(js_cocos2dx_Scheduler_isTargetPaused)); #if STANDALONE_TEST cls->defineFunction("addChild", _SE(Node_addChild)); cls->defineFunction("getChildren", _SE(Node_getChildren)); cls->defineFinalizeFunction(_SE(Node_finalized)); cls->install(); __jsb_Node_proto = cls->getProto(); __jsb_Node_class = cls; __jsb_Node_proto->defineFunction("foo", _SE(Node_foo)); __jsb_Node_proto->setProperty("var1", se::Value("I'm var1")); __jsb_Node_proto->setProperty("var2", se::Value(10000.323)); #endif ScriptingCore::getInstance()->setNodeEventListener(onReceiveNodeEvent); se::ScriptEngine::getInstance()->clearException(); return true; }