rngine 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/typescript/src/GameEngine.d.ts +1 -1
- package/lib/typescript/src/GameEngine.d.ts.map +1 -1
- package/lib/typescript/src/GameMethods.d.ts +2 -2
- package/lib/typescript/src/GameMethods.d.ts.map +1 -1
- package/lib/typescript/src/GameMethods.nitro.d.ts +1 -1
- package/lib/typescript/src/GameMethods.nitro.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +4 -4
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +1 -1
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/nitrogen/generated/android/c++/views/JHybridGameEngineStateUpdater.cpp +34 -17
- package/nitrogen/generated/android/c++/views/JHybridGameEngineStateUpdater.hpp +6 -1
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/rngine/HybridGameEngineSpec.kt +2 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/rngine/views/HybridGameEngineManager.kt +26 -10
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/rngine/views/HybridGameEngineStateUpdater.kt +2 -2
- package/nitrogen/generated/ios/c++/views/HybridGameEngineComponent.mm +40 -19
- package/nitrogen/generated/shared/c++/views/HybridGameEngineComponent.cpp +5 -45
- package/nitrogen/generated/shared/c++/views/HybridGameEngineComponent.hpp +20 -48
- package/package.json +21 -63
- package/shared/GameLoop.cpp +2 -0
- package/shared/GameRenderer.cpp +59 -47
- package/shared/GameRenderer.hpp +4 -0
- package/android/kls_database.db +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { GameEngineMethods, GameEngineProps } from './GameEngine.nitro';
|
|
1
|
+
import type { GameEngineMethods, GameEngineProps } from './GameEngine.nitro.js';
|
|
2
2
|
/**
|
|
3
3
|
* A game engine view component that renders the game world.
|
|
4
4
|
* Size it using the style prop like any other React Native view.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GameEngine.d.ts","sourceRoot":"","sources":["../../../src/GameEngine.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"GameEngine.d.ts","sourceRoot":"","sources":["../../../src/GameEngine.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,uBAAoB,CAAC;AAE7E;;;;;;;;GAQG;;AACH,wBAGE"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Entity, EntityUpdate, ScreenUpdate } from './nativeTypes';
|
|
2
|
-
import type { Config } from './types';
|
|
1
|
+
import type { Entity, EntityUpdate, ScreenUpdate } from './nativeTypes.js';
|
|
2
|
+
import type { Config } from './types.js';
|
|
3
3
|
/** Sets up the game engine with the given configuration. Call this before anything else. */
|
|
4
4
|
export declare const configure: ({ world, screen, entities, systems, paused, }: Config) => void;
|
|
5
5
|
/** Pauses the game loop. Systems will stop running. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GameMethods.d.ts","sourceRoot":"","sources":["../../../src/GameMethods.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,EAAU,MAAM,
|
|
1
|
+
{"version":3,"file":"GameMethods.d.ts","sourceRoot":"","sources":["../../../src/GameMethods.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,EAAU,MAAM,kBAAe,CAAC;AAChF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAS,CAAC;AAGtC,4FAA4F;AAC5F,eAAO,MAAM,SAAS,GAAI,+CAMvB,MAAM,SAyBR,CAAC;AAEF,uDAAuD;AACvD,eAAO,MAAM,KAAK,YAA4B,CAAC;AAE/C,6BAA6B;AAC7B,eAAO,MAAM,MAAM,YAA6B,CAAC;AAEjD,0EAA0E;AAC1E,eAAO,MAAM,KAAK,GAAI,UAAU,MAAM,GAAG,MAAM,EAAE,SAGhD,CAAC;AAEF,2HAA2H;AAC3H,eAAO,MAAM,OAAO,GAAI,IAAI,MAAM,SAA4B,CAAC;AAE/D,+HAA+H;AAC/H,eAAO,MAAM,YAAY,GAAI,cAAc,YAAY,SAEtD,CAAC;AAEF,oGAAoG;AACpG,eAAO,MAAM,cAAc,GAAI,eAAe,YAAY,EAAE,SAE3D,CAAC;AAEF,gEAAgE;AAChE,eAAO,MAAM,YAAY,GAAI,cAAc,YAAY,SAEtD,CAAC;AA2CF;;yEAEyE;AACzE,eAAO,MAAM,UAAU,GAAU,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChE,QAAQ,CAAC,KACR,OAAO,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,GAAG,MAAM,GAAE,CASpC,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { HybridObject } from 'react-native-nitro-modules';
|
|
2
|
-
import type { Entity, EntityUpdate, System, Screen, World, ScreenUpdate } from './nativeTypes';
|
|
2
|
+
import type { Entity, EntityUpdate, System, Screen, World, ScreenUpdate } from './nativeTypes.js';
|
|
3
3
|
interface GameMethods extends HybridObject<{
|
|
4
4
|
ios: 'c++';
|
|
5
5
|
android: 'c++';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GameMethods.nitro.d.ts","sourceRoot":"","sources":["../../../src/GameMethods.nitro.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,KAAK,EACV,MAAM,EACN,YAAY,EACZ,MAAM,EACN,MAAM,EACN,KAAK,EACL,YAAY,EACb,MAAM,
|
|
1
|
+
{"version":3,"file":"GameMethods.nitro.d.ts","sourceRoot":"","sources":["../../../src/GameMethods.nitro.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,KAAK,EACV,MAAM,EACN,YAAY,EACZ,MAAM,EACN,MAAM,EACN,KAAK,EACL,YAAY,EACb,MAAM,kBAAe,CAAC;AAGvB,UAAU,WAAY,SAAQ,YAAY,CAAC;IACzC,GAAG,EAAE,KAAK,CAAC;IACX,OAAO,EAAE,KAAK,CAAC;CAChB,CAAC;IACA,4BAA4B;IAC5B,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAC7B,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACtC,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAEpC,2BAA2B;IAC3B,KAAK,IAAI,IAAI,CAAC;IACd,MAAM,IAAI,IAAI,CAAC;IAEf,4BAA4B;IAC5B,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAChC,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,CAAC,aAAa,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC;IAEpD,4BAA4B;IAC5B,YAAY,CAAC,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;IAE/C,oBAAoB;IACpB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;IACnC,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1D,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC3D;AAED,eAAO,MAAM,WAAW,aACqC,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import GameEngine from './GameEngine';
|
|
2
|
-
export * from './GameMethods';
|
|
3
|
-
export * from './types';
|
|
4
|
-
export type { World, Screen, Entity, CollisionPair, Collision, EntityUpdate, SystemContext, } from './nativeTypes';
|
|
1
|
+
import GameEngine from './GameEngine.js';
|
|
2
|
+
export * from './GameMethods.js';
|
|
3
|
+
export * from './types.js';
|
|
4
|
+
export type { World, Screen, Entity, CollisionPair, Collision, EntityUpdate, SystemContext, } from './nativeTypes.js';
|
|
5
5
|
export { GameEngine };
|
|
6
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,iBAAc,CAAC;AAEtC,cAAc,kBAAe,CAAC;AAC9B,cAAc,YAAS,CAAC;AACxB,YAAY,EACV,KAAK,EACL,MAAM,EACN,MAAM,EACN,aAAa,EACb,SAAS,EACT,YAAY,EACZ,aAAa,GACd,MAAM,kBAAe,CAAC;AACvB,OAAO,EAAE,UAAU,EAAE,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { System as NativeSystem, Screen, Entity, World, SystemContext } from './nativeTypes';
|
|
1
|
+
import type { System as NativeSystem, Screen, Entity, World, SystemContext } from './nativeTypes.js';
|
|
2
2
|
export type System = Omit<NativeSystem, 'onTick'> & {
|
|
3
3
|
/** Called every tick with the resolved entities and collisions. */
|
|
4
4
|
onTick: (systemContext: SystemContext) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,IAAI,YAAY,EACtB,MAAM,EACN,MAAM,EACN,KAAK,EACL,aAAa,EACd,MAAM,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,IAAI,YAAY,EACtB,MAAM,EACN,MAAM,EACN,KAAK,EACL,aAAa,EACd,MAAM,kBAAe,CAAC;AAEvB,MAAM,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,GAAG;IAClD,mEAAmE;IACnE,MAAM,EAAE,CAAC,aAAa,EAAE,aAAa,KAAK,IAAI,CAAC;CAChD,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG;IACnB,6CAA6C;IAC7C,KAAK,EAAE,KAAK,CAAC;IACb,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,8GAA8G;IAC9G,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,wDAAwD;IACxD,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC"}
|
|
@@ -15,38 +15,55 @@ namespace margelo::nitro::rngine::views {
|
|
|
15
15
|
using namespace facebook;
|
|
16
16
|
using ConcreteStateData = react::ConcreteState<HybridGameEngineState>;
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
std::shared_ptr<const HybridGameEngineProps> JHybridGameEngineStateUpdater::getPropsFromStateWrapper(
|
|
19
|
+
jni::alias_ref<JStateWrapper::javaobject> stateWrapper) {
|
|
20
|
+
if (stateWrapper.get() == nullptr) {
|
|
21
|
+
return nullptr;
|
|
22
|
+
}
|
|
23
23
|
// Get concrete StateWrapperImpl from passed StateWrapper interface object
|
|
24
|
-
jobject rawStateWrapper =
|
|
25
|
-
if (!
|
|
26
|
-
|
|
24
|
+
jobject rawStateWrapper = stateWrapper.get();
|
|
25
|
+
if (!stateWrapper->isInstanceOf(react::StateWrapperImpl::javaClassStatic())) [[unlikely]] {
|
|
26
|
+
throw std::runtime_error("StateWrapper is not a StateWrapperImpl");
|
|
27
|
+
}
|
|
28
|
+
auto stateWrapperImpl = jni::alias_ref<react::StateWrapperImpl::javaobject>{
|
|
29
|
+
static_cast<react::StateWrapperImpl::javaobject>(rawStateWrapper)
|
|
30
|
+
};
|
|
31
|
+
std::shared_ptr<const react::State> state = stateWrapperImpl->cthis()->getState();
|
|
32
|
+
if (state == nullptr) {
|
|
33
|
+
return nullptr;
|
|
27
34
|
}
|
|
28
|
-
auto stateWrapper = jni::alias_ref<react::StateWrapperImpl::javaobject>{
|
|
29
|
-
static_cast<react::StateWrapperImpl::javaobject>(rawStateWrapper)};
|
|
30
|
-
std::shared_ptr<const react::State> state = stateWrapper->cthis()->getState();
|
|
31
35
|
auto concreteState = std::static_pointer_cast<const ConcreteStateData>(state);
|
|
32
36
|
const HybridGameEngineState& data = concreteState->getData();
|
|
33
|
-
const std::shared_ptr<HybridGameEngineProps>& props = data.getProps();
|
|
37
|
+
const std::shared_ptr<const HybridGameEngineProps>& props = data.getProps();
|
|
34
38
|
if (props == nullptr) [[unlikely]] {
|
|
35
|
-
// Props aren't set yet!
|
|
36
39
|
throw std::runtime_error("HybridGameEngineState's data doesn't contain any props!");
|
|
37
40
|
}
|
|
41
|
+
return props;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
void JHybridGameEngineStateUpdater::updateViewProps(jni::alias_ref<jni::JClass> /* class */,
|
|
45
|
+
jni::alias_ref<JHybridGameEngineSpec::JavaPart> javaView,
|
|
46
|
+
jni::alias_ref<JStateWrapper::javaobject> newState,
|
|
47
|
+
jni::alias_ref<JStateWrapper::javaobject> oldState) {
|
|
48
|
+
std::shared_ptr<JHybridGameEngineSpec> hybridView = javaView->getJHybridGameEngineSpec();
|
|
49
|
+
std::shared_ptr<const HybridGameEngineProps> newProps = getPropsFromStateWrapper(newState);
|
|
50
|
+
std::shared_ptr<const HybridGameEngineProps> oldProps = getPropsFromStateWrapper(oldState);
|
|
51
|
+
if (newProps == nullptr) [[unlikely]] {
|
|
52
|
+
throw std::runtime_error("Current StateWrapper doesn't contain any props!");
|
|
53
|
+
}
|
|
38
54
|
|
|
39
|
-
// Update
|
|
55
|
+
// Update only props that differ from the previous State snapshot.
|
|
40
56
|
|
|
41
57
|
|
|
42
58
|
// Update hybridRef if it changed
|
|
43
|
-
if (
|
|
59
|
+
if (oldProps == nullptr
|
|
60
|
+
? newProps->hybridRef.isProvided()
|
|
61
|
+
: !newProps->hybridRef.hasSameValue(oldProps->hybridRef)) {
|
|
44
62
|
// hybridRef changed - call it with new this
|
|
45
|
-
const auto& maybeFunc =
|
|
63
|
+
const auto& maybeFunc = newProps->hybridRef.get();
|
|
46
64
|
if (maybeFunc.has_value()) {
|
|
47
65
|
maybeFunc.value()(hybridView);
|
|
48
66
|
}
|
|
49
|
-
props->hybridRef.isDirty = false;
|
|
50
67
|
}
|
|
51
68
|
}
|
|
52
69
|
|
|
@@ -31,7 +31,12 @@ public:
|
|
|
31
31
|
public:
|
|
32
32
|
static void updateViewProps(jni::alias_ref<jni::JClass> /* class */,
|
|
33
33
|
jni::alias_ref<JHybridGameEngineSpec::JavaPart> view,
|
|
34
|
-
jni::alias_ref<JStateWrapper::javaobject>
|
|
34
|
+
jni::alias_ref<JStateWrapper::javaobject> newState,
|
|
35
|
+
jni::alias_ref<JStateWrapper::javaobject> oldState);
|
|
36
|
+
|
|
37
|
+
private:
|
|
38
|
+
static std::shared_ptr<const HybridGameEngineProps> getPropsFromStateWrapper(
|
|
39
|
+
jni::alias_ref<JStateWrapper::javaobject> stateWrapper);
|
|
35
40
|
|
|
36
41
|
public:
|
|
37
42
|
static void registerNatives() {
|
|
@@ -10,6 +10,7 @@ package com.margelo.nitro.rngine
|
|
|
10
10
|
import androidx.annotation.Keep
|
|
11
11
|
import com.facebook.jni.HybridData
|
|
12
12
|
import com.facebook.proguard.annotations.DoNotStrip
|
|
13
|
+
import dalvik.annotation.optimization.FastNative
|
|
13
14
|
import com.margelo.nitro.core.HybridObject
|
|
14
15
|
import com.margelo.nitro.views.HybridView
|
|
15
16
|
|
|
@@ -41,6 +42,7 @@ abstract class HybridGameEngineSpec: HybridView() {
|
|
|
41
42
|
@Keep
|
|
42
43
|
protected open class CxxPart(javaPart: HybridGameEngineSpec): HybridObject.CxxPart(javaPart) {
|
|
43
44
|
// C++ JHybridGameEngineSpec::CxxPart::initHybrid(...)
|
|
45
|
+
@FastNative
|
|
44
46
|
external override fun initHybrid(): HybridData
|
|
45
47
|
}
|
|
46
48
|
override fun createCxxPart(): CxxPart {
|
package/nitrogen/generated/android/kotlin/com/margelo/nitro/rngine/views/HybridGameEngineManager.kt
CHANGED
|
@@ -20,6 +20,14 @@ import com.margelo.nitro.rngine.*
|
|
|
20
20
|
* Represents the React Native `ViewManager` for the "GameEngine" Nitro HybridView.
|
|
21
21
|
*/
|
|
22
22
|
public class HybridGameEngineManager: SimpleViewManager<View>() {
|
|
23
|
+
/**
|
|
24
|
+
* Represents the View and its last state snapshot (mutable)
|
|
25
|
+
*/
|
|
26
|
+
private class HybridViewHolder(
|
|
27
|
+
val hybridView: GameEngine,
|
|
28
|
+
var lastState: StateWrapper? = null,
|
|
29
|
+
)
|
|
30
|
+
|
|
23
31
|
init {
|
|
24
32
|
if (RecyclableView::class.java.isAssignableFrom(GameEngine::class.java)) {
|
|
25
33
|
// Enable view recycling
|
|
@@ -34,33 +42,41 @@ public class HybridGameEngineManager: SimpleViewManager<View>() {
|
|
|
34
42
|
override fun createViewInstance(reactContext: ThemedReactContext): View {
|
|
35
43
|
val hybridView = GameEngine(reactContext)
|
|
36
44
|
val view = hybridView.view
|
|
37
|
-
view.setTag(associated_hybrid_view_tag, hybridView)
|
|
45
|
+
view.setTag(associated_hybrid_view_tag, HybridViewHolder(hybridView))
|
|
38
46
|
return view
|
|
39
47
|
}
|
|
40
48
|
|
|
41
49
|
override fun updateState(view: View, props: ReactStylesDiffMap, stateWrapper: StateWrapper): Any? {
|
|
42
|
-
val
|
|
50
|
+
val holder = getHybridViewHolder(view)
|
|
43
51
|
?: throw Error("Couldn't find view $view in local views table!")
|
|
52
|
+
val hybridView = holder.hybridView
|
|
53
|
+
val oldState = holder.lastState
|
|
54
|
+
val newState = stateWrapper
|
|
44
55
|
|
|
45
56
|
// 1. Update each prop individually
|
|
46
57
|
hybridView.beforeUpdate()
|
|
47
|
-
HybridGameEngineStateUpdater.updateViewProps(hybridView,
|
|
58
|
+
HybridGameEngineStateUpdater.updateViewProps(hybridView, newState, oldState)
|
|
48
59
|
hybridView.afterUpdate()
|
|
60
|
+
holder.lastState = newState
|
|
49
61
|
|
|
50
62
|
// 2. Continue in base View props
|
|
51
|
-
return super.updateState(view, props,
|
|
63
|
+
return super.updateState(view, props, newState)
|
|
52
64
|
}
|
|
53
65
|
|
|
54
66
|
override fun onDropViewInstance(view: View) {
|
|
55
|
-
val
|
|
56
|
-
|
|
67
|
+
val holder = getHybridViewHolder(view)
|
|
68
|
+
holder?.lastState = null
|
|
69
|
+
holder?.hybridView?.onDropView()
|
|
57
70
|
return super.onDropViewInstance(view)
|
|
58
71
|
}
|
|
59
72
|
|
|
60
73
|
protected override fun prepareToRecycleView(reactContext: ThemedReactContext, view: View): View? {
|
|
61
|
-
super.prepareToRecycleView(reactContext, view)
|
|
62
|
-
|
|
74
|
+
val preparedView = super.prepareToRecycleView(reactContext, view)
|
|
75
|
+
?: return null
|
|
76
|
+
val holder = getHybridViewHolder(preparedView)
|
|
63
77
|
?: return null
|
|
78
|
+
val hybridView = holder.hybridView
|
|
79
|
+
holder.lastState = null
|
|
64
80
|
|
|
65
81
|
@Suppress("USELESS_IS_CHECK")
|
|
66
82
|
if (hybridView is RecyclableView) {
|
|
@@ -74,7 +90,7 @@ public class HybridGameEngineManager: SimpleViewManager<View>() {
|
|
|
74
90
|
}
|
|
75
91
|
}
|
|
76
92
|
|
|
77
|
-
private fun
|
|
78
|
-
return view.getTag(associated_hybrid_view_tag) as?
|
|
93
|
+
private fun getHybridViewHolder(view: View): HybridViewHolder? {
|
|
94
|
+
return view.getTag(associated_hybrid_view_tag) as? HybridViewHolder
|
|
79
95
|
}
|
|
80
96
|
}
|
|
@@ -14,10 +14,10 @@ internal class HybridGameEngineStateUpdater {
|
|
|
14
14
|
companion object {
|
|
15
15
|
/**
|
|
16
16
|
* Updates the props for [view] through C++.
|
|
17
|
-
* The [
|
|
17
|
+
* The [newState] prop is expected to contain [view]'s props as wrapped Fabric state.
|
|
18
18
|
*/
|
|
19
19
|
@Suppress("KotlinJniMissingFunction")
|
|
20
20
|
@JvmStatic
|
|
21
|
-
external fun updateViewProps(view: HybridGameEngineSpec,
|
|
21
|
+
external fun updateViewProps(view: HybridGameEngineSpec, newState: StateWrapper, oldState: StateWrapper?)
|
|
22
22
|
}
|
|
23
23
|
}
|
|
@@ -37,6 +37,7 @@ using namespace margelo::nitro::rngine::views;
|
|
|
37
37
|
|
|
38
38
|
@implementation HybridGameEngineComponent {
|
|
39
39
|
std::shared_ptr<HybridGameEngineSpecSwift> _hybridView;
|
|
40
|
+
BOOL _didDropView;
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
+ (void) load {
|
|
@@ -50,6 +51,7 @@ using namespace margelo::nitro::rngine::views;
|
|
|
50
51
|
|
|
51
52
|
- (instancetype) init {
|
|
52
53
|
if (self = [super init]) {
|
|
54
|
+
_props = HybridGameEngineShadowNode::defaultSharedProps();
|
|
53
55
|
std::shared_ptr<HybridGameEngineSpec> hybridView = Rngine::RngineAutolinking::createGameEngine();
|
|
54
56
|
_hybridView = std::dynamic_pointer_cast<HybridGameEngineSpecSwift>(hybridView);
|
|
55
57
|
[self updateView];
|
|
@@ -69,31 +71,50 @@ using namespace margelo::nitro::rngine::views;
|
|
|
69
71
|
[self setContentView:view];
|
|
70
72
|
}
|
|
71
73
|
|
|
74
|
+
- (void) notifyOnDropView {
|
|
75
|
+
// A recycled component can later be invalidated. Notify only once per mount.
|
|
76
|
+
if (_didDropView) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
Rngine::HybridGameEngineSpec_cxx& swiftPart = _hybridView->getSwiftPart();
|
|
80
|
+
swiftPart.onDropView();
|
|
81
|
+
_didDropView = YES;
|
|
82
|
+
}
|
|
83
|
+
|
|
72
84
|
- (void) updateProps:(const std::shared_ptr<const react::Props>&)props
|
|
73
85
|
oldProps:(const std::shared_ptr<const react::Props>&)oldProps {
|
|
86
|
+
// A props update marks a newly mounted or still-active component.
|
|
87
|
+
_didDropView = NO;
|
|
88
|
+
|
|
74
89
|
// 1. Downcast props
|
|
75
|
-
const auto&
|
|
76
|
-
auto
|
|
90
|
+
const auto& newViewProps = *std::static_pointer_cast<const HybridGameEngineProps>(props);
|
|
91
|
+
const auto* oldViewProps = static_cast<const HybridGameEngineProps*>(oldProps.get());
|
|
77
92
|
Rngine::HybridGameEngineSpec_cxx& swiftPart = _hybridView->getSwiftPart();
|
|
78
93
|
|
|
79
|
-
// 2. Update
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
// hybridRef
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
94
|
+
// 2. Update only props that differ from the previous Props snapshot.
|
|
95
|
+
const bool hasTransactionPropChanges = oldViewProps == nullptr
|
|
96
|
+
? newViewProps.hasAnyProvidedProps()
|
|
97
|
+
: !newViewProps.hasSameProps(*oldViewProps);
|
|
98
|
+
if (hasTransactionPropChanges) {
|
|
99
|
+
swiftPart.beforeUpdate();
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
// Update hybridRef if it changed
|
|
104
|
+
if (oldViewProps == nullptr
|
|
105
|
+
? newViewProps.hybridRef.isProvided()
|
|
106
|
+
: !newViewProps.hybridRef.hasSameValue(oldViewProps->hybridRef)) {
|
|
107
|
+
// hybridRef changed - call it with new this
|
|
108
|
+
const auto& maybeFunc = newViewProps.hybridRef.get();
|
|
109
|
+
if (maybeFunc.has_value()) {
|
|
110
|
+
maybeFunc.value()(_hybridView);
|
|
111
|
+
}
|
|
92
112
|
}
|
|
93
|
-
|
|
113
|
+
|
|
114
|
+
swiftPart.afterUpdate();
|
|
94
115
|
}
|
|
95
116
|
|
|
96
|
-
//
|
|
117
|
+
// 3. Continue in base class
|
|
97
118
|
[super updateProps:props oldProps:oldProps];
|
|
98
119
|
}
|
|
99
120
|
|
|
@@ -102,6 +123,7 @@ using namespace margelo::nitro::rngine::views;
|
|
|
102
123
|
}
|
|
103
124
|
|
|
104
125
|
- (void)prepareForRecycle {
|
|
126
|
+
[self notifyOnDropView];
|
|
105
127
|
[super prepareForRecycle];
|
|
106
128
|
Rngine::HybridGameEngineSpec_cxx& swiftPart = _hybridView->getSwiftPart();
|
|
107
129
|
swiftPart.maybePrepareForRecycle();
|
|
@@ -109,8 +131,7 @@ using namespace margelo::nitro::rngine::views;
|
|
|
109
131
|
|
|
110
132
|
#ifdef ENABLE_RCT_COMPONENT_VIEW_INVALIDATE
|
|
111
133
|
- (void)invalidate {
|
|
112
|
-
|
|
113
|
-
swiftPart.onDropView();
|
|
134
|
+
[self notifyOnDropView];
|
|
114
135
|
[super invalidate];
|
|
115
136
|
}
|
|
116
137
|
#endif
|
|
@@ -7,35 +7,20 @@
|
|
|
7
7
|
|
|
8
8
|
#include "HybridGameEngineComponent.hpp"
|
|
9
9
|
|
|
10
|
-
#include <
|
|
11
|
-
#include <
|
|
12
|
-
#include <utility>
|
|
13
|
-
#include <NitroModules/NitroDefines.hpp>
|
|
14
|
-
#include <NitroModules/JSIConverter.hpp>
|
|
15
|
-
#include <NitroModules/PropNameIDCache.hpp>
|
|
16
|
-
#include <react/renderer/core/RawValue.h>
|
|
17
|
-
#include <react/renderer/core/ShadowNode.h>
|
|
18
|
-
#include <react/renderer/core/ComponentDescriptor.h>
|
|
19
|
-
#include <react/renderer/components/view/ViewProps.h>
|
|
10
|
+
#include <NitroModules/NitroHash.hpp>
|
|
11
|
+
#include <NitroModules/ReactProp.hpp>
|
|
20
12
|
|
|
21
13
|
namespace margelo::nitro::rngine::views {
|
|
22
14
|
|
|
15
|
+
using namespace facebook;
|
|
16
|
+
|
|
23
17
|
extern const char HybridGameEngineComponentName[] = "GameEngine";
|
|
24
18
|
|
|
25
19
|
HybridGameEngineProps::HybridGameEngineProps(const react::PropsParserContext& context,
|
|
26
20
|
const HybridGameEngineProps& sourceProps,
|
|
27
21
|
const react::RawProps& rawProps):
|
|
28
22
|
react::ViewProps(context, sourceProps, rawProps, filterObjectKeys),
|
|
29
|
-
hybridRef(
|
|
30
|
-
try {
|
|
31
|
-
const react::RawValue* rawValue = rawProps.at("hybridRef", nullptr, nullptr);
|
|
32
|
-
if (rawValue == nullptr) return sourceProps.hybridRef;
|
|
33
|
-
const auto& [runtime, value] = (std::pair<jsi::Runtime*, jsi::Value>)*rawValue;
|
|
34
|
-
return CachedProp<std::optional<std::function<void(const std::shared_ptr<HybridGameEngineSpec>& /* ref */)>>>::fromRawValue(*runtime, value.asObject(*runtime).getProperty(*runtime, PropNameIDCache::get(*runtime, "f")), sourceProps.hybridRef);
|
|
35
|
-
} catch (const std::exception& exc) {
|
|
36
|
-
throw std::runtime_error(std::string("GameEngine.hybridRef: ") + exc.what());
|
|
37
|
-
}
|
|
38
|
-
}()) { }
|
|
23
|
+
hybridRef(nitro::ReactProp<std::optional<std::function<void(const std::shared_ptr<HybridGameEngineSpec>& /* ref */)>>>::fromRawValue("GameEngine", "hybridRef", rawProps, sourceProps.hybridRef)) { }
|
|
39
24
|
|
|
40
25
|
bool HybridGameEngineProps::filterObjectKeys(const std::string& propName) {
|
|
41
26
|
switch (hashString(propName)) {
|
|
@@ -44,29 +29,4 @@ namespace margelo::nitro::rngine::views {
|
|
|
44
29
|
}
|
|
45
30
|
}
|
|
46
31
|
|
|
47
|
-
HybridGameEngineComponentDescriptor::HybridGameEngineComponentDescriptor(const react::ComponentDescriptorParameters& parameters)
|
|
48
|
-
: ConcreteComponentDescriptor(parameters,
|
|
49
|
-
react::RawPropsParser(/* enableJsiParser */ true)) {}
|
|
50
|
-
|
|
51
|
-
std::shared_ptr<const react::Props> HybridGameEngineComponentDescriptor::cloneProps(const react::PropsParserContext& context,
|
|
52
|
-
const std::shared_ptr<const react::Props>& props,
|
|
53
|
-
react::RawProps rawProps) const {
|
|
54
|
-
// 1. Prepare raw props parser
|
|
55
|
-
rawProps.parse(rawPropsParser_);
|
|
56
|
-
// 2. Copy props with Nitro's cached copy constructor
|
|
57
|
-
return HybridGameEngineShadowNode::Props(context, /* & */ rawProps, props);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
#ifdef ANDROID
|
|
61
|
-
void HybridGameEngineComponentDescriptor::adopt(react::ShadowNode& shadowNode) const {
|
|
62
|
-
// This is called immediately after `ShadowNode` is created, cloned or in progress.
|
|
63
|
-
// On Android, we need to wrap props in our state, which gets routed through Java and later unwrapped in JNI/C++.
|
|
64
|
-
auto& concreteShadowNode = static_cast<HybridGameEngineShadowNode&>(shadowNode);
|
|
65
|
-
const std::shared_ptr<const HybridGameEngineProps>& constProps = concreteShadowNode.getConcreteSharedProps();
|
|
66
|
-
const std::shared_ptr<HybridGameEngineProps>& props = std::const_pointer_cast<HybridGameEngineProps>(constProps);
|
|
67
|
-
HybridGameEngineState state{props};
|
|
68
|
-
concreteShadowNode.setStateData(std::move(state));
|
|
69
|
-
}
|
|
70
|
-
#endif
|
|
71
|
-
|
|
72
32
|
} // namespace margelo::nitro::rngine::views
|
|
@@ -7,14 +7,15 @@
|
|
|
7
7
|
|
|
8
8
|
#pragma once
|
|
9
9
|
|
|
10
|
-
#include <
|
|
11
|
-
#include <NitroModules/
|
|
12
|
-
#include <NitroModules/
|
|
13
|
-
#include <NitroModules/CachedProp.hpp>
|
|
14
|
-
#include <react/renderer/core/ConcreteComponentDescriptor.h>
|
|
15
|
-
#include <react/renderer/core/PropsParserContext.h>
|
|
10
|
+
#include <NitroModules/ReactProp.hpp>
|
|
11
|
+
#include <NitroModules/ViewComponentDescriptor.hpp>
|
|
12
|
+
#include <NitroModules/ViewPropsHolderState.hpp>
|
|
16
13
|
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
|
|
17
14
|
#include <react/renderer/components/view/ViewProps.h>
|
|
15
|
+
#include <react/renderer/core/PropsParserContext.h>
|
|
16
|
+
#include <react/renderer/core/RawProps.h>
|
|
17
|
+
|
|
18
|
+
#include <string>
|
|
18
19
|
|
|
19
20
|
#include <memory>
|
|
20
21
|
#include "HybridGameEngineSpec.hpp"
|
|
@@ -41,7 +42,17 @@ namespace margelo::nitro::rngine::views {
|
|
|
41
42
|
const react::RawProps& rawProps);
|
|
42
43
|
|
|
43
44
|
public:
|
|
44
|
-
|
|
45
|
+
nitro::ReactProp<std::optional<std::function<void(const std::shared_ptr<HybridGameEngineSpec>& /* ref */)>>> hybridRef;
|
|
46
|
+
|
|
47
|
+
[[nodiscard]]
|
|
48
|
+
bool hasSameProps(const HybridGameEngineProps& other) const noexcept {
|
|
49
|
+
return hybridRef.hasSameValue(other.hybridRef);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
[[nodiscard]]
|
|
53
|
+
bool hasAnyProvidedProps() const noexcept {
|
|
54
|
+
return hybridRef.isProvided();
|
|
55
|
+
}
|
|
45
56
|
|
|
46
57
|
private:
|
|
47
58
|
static bool filterObjectKeys(const std::string& propName);
|
|
@@ -50,32 +61,7 @@ namespace margelo::nitro::rngine::views {
|
|
|
50
61
|
/**
|
|
51
62
|
* State for the "GameEngine" View.
|
|
52
63
|
*/
|
|
53
|
-
|
|
54
|
-
public:
|
|
55
|
-
HybridGameEngineState() = default;
|
|
56
|
-
explicit HybridGameEngineState(const std::shared_ptr<HybridGameEngineProps>& props):
|
|
57
|
-
_props(props) {}
|
|
58
|
-
|
|
59
|
-
public:
|
|
60
|
-
[[nodiscard]]
|
|
61
|
-
const std::shared_ptr<HybridGameEngineProps>& getProps() const {
|
|
62
|
-
return _props;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
public:
|
|
66
|
-
#ifdef ANDROID
|
|
67
|
-
HybridGameEngineState(const HybridGameEngineState& /* previousState */, folly::dynamic /* data */) {}
|
|
68
|
-
folly::dynamic getDynamic() const {
|
|
69
|
-
throw std::runtime_error("HybridGameEngineState does not support folly!");
|
|
70
|
-
}
|
|
71
|
-
react::MapBuffer getMapBuffer() const {
|
|
72
|
-
throw std::runtime_error("HybridGameEngineState does not support MapBuffer!");
|
|
73
|
-
};
|
|
74
|
-
#endif
|
|
75
|
-
|
|
76
|
-
private:
|
|
77
|
-
std::shared_ptr<HybridGameEngineProps> _props;
|
|
78
|
-
};
|
|
64
|
+
using HybridGameEngineState = nitro::ViewPropsHolderState<HybridGameEngineProps>;
|
|
79
65
|
|
|
80
66
|
/**
|
|
81
67
|
* The Shadow Node for the "GameEngine" View.
|
|
@@ -88,21 +74,7 @@ namespace margelo::nitro::rngine::views {
|
|
|
88
74
|
/**
|
|
89
75
|
* The Component Descriptor for the "GameEngine" View.
|
|
90
76
|
*/
|
|
91
|
-
|
|
92
|
-
public:
|
|
93
|
-
explicit HybridGameEngineComponentDescriptor(const react::ComponentDescriptorParameters& parameters);
|
|
94
|
-
|
|
95
|
-
public:
|
|
96
|
-
/**
|
|
97
|
-
* A faster path for cloning props - reuses the caching logic from `HybridGameEngineProps`.
|
|
98
|
-
*/
|
|
99
|
-
std::shared_ptr<const react::Props> cloneProps(const react::PropsParserContext& context,
|
|
100
|
-
const std::shared_ptr<const react::Props>& props,
|
|
101
|
-
react::RawProps rawProps) const override;
|
|
102
|
-
#ifdef ANDROID
|
|
103
|
-
void adopt(react::ShadowNode& shadowNode) const override;
|
|
104
|
-
#endif
|
|
105
|
-
};
|
|
77
|
+
using HybridGameEngineComponentDescriptor = nitro::ViewComponentDescriptor<HybridGameEngineShadowNode>;
|
|
106
78
|
|
|
107
79
|
/* The actual view for "GameEngine" needs to be implemented in platform-specific code. */
|
|
108
80
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rngine",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "React Native 2D game engine powered by Nitro Modules",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
|
@@ -28,9 +28,6 @@
|
|
|
28
28
|
"!android/gradlew",
|
|
29
29
|
"!android/gradlew.bat",
|
|
30
30
|
"!android/local.properties",
|
|
31
|
-
"!**/__tests__",
|
|
32
|
-
"!**/__fixtures__",
|
|
33
|
-
"!**/__mocks__",
|
|
34
31
|
"!**/.*"
|
|
35
32
|
],
|
|
36
33
|
"scripts": {
|
|
@@ -39,11 +36,7 @@
|
|
|
39
36
|
"prepare": "bob build && node scripts/nitrogen-patch.js",
|
|
40
37
|
"nitrogen": "nitrogen",
|
|
41
38
|
"typecheck": "tsc",
|
|
42
|
-
"lint": "eslint \"**/*.{js,ts,tsx}\""
|
|
43
|
-
"test": "jest",
|
|
44
|
-
"release": "release-it --only-version",
|
|
45
|
-
"web": "vite",
|
|
46
|
-
"build:web": "vite build"
|
|
39
|
+
"lint": "eslint \"**/*.{js,ts,tsx}\""
|
|
47
40
|
},
|
|
48
41
|
"keywords": [
|
|
49
42
|
"react-native",
|
|
@@ -64,39 +57,33 @@
|
|
|
64
57
|
"registry": "https://registry.npmjs.org/"
|
|
65
58
|
},
|
|
66
59
|
"devDependencies": {
|
|
67
|
-
"@commitlint/config-conventional": "^
|
|
68
|
-
"@eslint/compat": "^2.
|
|
69
|
-
"@eslint/eslintrc": "^3.3.
|
|
60
|
+
"@commitlint/config-conventional": "^21.2.3",
|
|
61
|
+
"@eslint/compat": "^2.1.1",
|
|
62
|
+
"@eslint/eslintrc": "^3.3.7",
|
|
70
63
|
"@eslint/js": "^10.0.1",
|
|
71
|
-
"@
|
|
72
|
-
"@react-native/
|
|
73
|
-
"@react
|
|
74
|
-
"
|
|
75
|
-
"@release-it/conventional-changelog": "^10.0.6",
|
|
76
|
-
"@types/react": "^19.2.0",
|
|
77
|
-
"commitlint": "^20.5.0",
|
|
64
|
+
"@react-native/babel-preset": "^0.87.1",
|
|
65
|
+
"@react-native/eslint-config": "^0.87.1",
|
|
66
|
+
"@types/react": "^19.3.0",
|
|
67
|
+
"commitlint": "^21.2.3",
|
|
78
68
|
"del-cli": "^7.0.0",
|
|
79
|
-
"eslint": "
|
|
69
|
+
"eslint": "9.39.5",
|
|
80
70
|
"eslint-config-prettier": "^10.1.8",
|
|
81
71
|
"eslint-plugin-ft-flow": "^3.0.11",
|
|
82
|
-
"eslint-plugin-prettier": "^5.5.
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
"prettier": "^3.8.1",
|
|
72
|
+
"eslint-plugin-prettier": "^5.5.6",
|
|
73
|
+
"lefthook": "^2.1.14",
|
|
74
|
+
"nitrogen": "^0.37.1",
|
|
75
|
+
"prettier": "^3.9.8",
|
|
87
76
|
"react": "19.2.3",
|
|
88
|
-
"react-native": "0.85.
|
|
89
|
-
"react-native-builder-bob": "^0.
|
|
90
|
-
"react-native-nitro-modules": "^0.
|
|
91
|
-
"
|
|
92
|
-
"release-it": "^19.2.4",
|
|
93
|
-
"turbo": "^2.8.21",
|
|
77
|
+
"react-native": "0.85.3",
|
|
78
|
+
"react-native-builder-bob": "^0.43.1",
|
|
79
|
+
"react-native-nitro-modules": "^0.37.1",
|
|
80
|
+
"turbo": "^2.11.2",
|
|
94
81
|
"typescript": "^6.0.2"
|
|
95
82
|
},
|
|
96
83
|
"peerDependencies": {
|
|
97
84
|
"react": "*",
|
|
98
85
|
"react-native": "*",
|
|
99
|
-
"react-native-nitro-modules": "^0.
|
|
86
|
+
"react-native-nitro-modules": "^0.37.1"
|
|
100
87
|
},
|
|
101
88
|
"workspaces": [
|
|
102
89
|
"example"
|
|
@@ -134,50 +121,21 @@
|
|
|
134
121
|
"trailingComma": "es5",
|
|
135
122
|
"useTabs": false
|
|
136
123
|
},
|
|
137
|
-
"jest": {
|
|
138
|
-
"preset": "@react-native/jest-preset",
|
|
139
|
-
"modulePathIgnorePatterns": [
|
|
140
|
-
"<rootDir>/example/node_modules",
|
|
141
|
-
"<rootDir>/lib/"
|
|
142
|
-
]
|
|
143
|
-
},
|
|
144
124
|
"commitlint": {
|
|
145
125
|
"extends": [
|
|
146
126
|
"@commitlint/config-conventional"
|
|
147
127
|
]
|
|
148
128
|
},
|
|
149
|
-
"release-it": {
|
|
150
|
-
"git": {
|
|
151
|
-
"commitMessage": "chore: release ${version}",
|
|
152
|
-
"tagName": "v${version}"
|
|
153
|
-
},
|
|
154
|
-
"npm": {
|
|
155
|
-
"publish": true
|
|
156
|
-
},
|
|
157
|
-
"github": {
|
|
158
|
-
"release": true
|
|
159
|
-
},
|
|
160
|
-
"plugins": {
|
|
161
|
-
"@release-it/conventional-changelog": {
|
|
162
|
-
"preset": {
|
|
163
|
-
"name": "angular"
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
},
|
|
168
129
|
"create-react-native-library": {
|
|
169
130
|
"type": "nitro-view",
|
|
170
131
|
"languages": "kotlin-swift",
|
|
171
132
|
"tools": [
|
|
172
133
|
"eslint",
|
|
173
|
-
"
|
|
174
|
-
"lefthook",
|
|
175
|
-
"release-it",
|
|
176
|
-
"vite"
|
|
134
|
+
"lefthook"
|
|
177
135
|
],
|
|
178
136
|
"version": "0.61.0"
|
|
179
137
|
},
|
|
180
138
|
"dependencies": {
|
|
181
|
-
"react-native-skia-android": "^
|
|
139
|
+
"react-native-skia-android": "^154.0.0"
|
|
182
140
|
}
|
|
183
141
|
}
|
package/shared/GameLoop.cpp
CHANGED
package/shared/GameRenderer.cpp
CHANGED
|
@@ -43,63 +43,85 @@ GameRenderer::~GameRenderer() {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
void GameRenderer::onSurfaceCreated(ANativeWindow *window) {
|
|
46
|
-
__android_log_print(ANDROID_LOG_INFO, "GameRenderer", "onSurfaceCreated");
|
|
47
46
|
|
|
48
|
-
|
|
47
|
+
std::lock_guard<std::mutex> lock(_taskMutex);
|
|
48
|
+
_taskQueue.push([this, window]() {
|
|
49
|
+
__android_log_print(ANDROID_LOG_INFO, "GameRenderer", "onSurfaceCreated");
|
|
49
50
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
_nativeWindow = window;
|
|
52
|
+
|
|
53
|
+
if (!_initialized) {
|
|
54
|
+
if (!initializeEGL()) {
|
|
55
|
+
__android_log_print(ANDROID_LOG_ERROR, "GameRenderer",
|
|
56
|
+
"onSurfaceCreated: EGL initialization failed");
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
_initialized = true;
|
|
55
60
|
}
|
|
56
|
-
_initialized = true;
|
|
57
|
-
}
|
|
58
61
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
if (!createEGLSurface()) {
|
|
63
|
+
__android_log_print(ANDROID_LOG_ERROR, "GameRenderer",
|
|
64
|
+
"onSurfaceCreated: EGL surface creation failed");
|
|
65
|
+
}
|
|
66
|
+
});
|
|
63
67
|
}
|
|
64
68
|
|
|
65
69
|
void GameRenderer::onSurfaceChanged(int width, int height) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
std::lock_guard<std::mutex> lock(_taskMutex);
|
|
71
|
+
_taskQueue.push([this, width, height]() {
|
|
72
|
+
_width = width;
|
|
73
|
+
_height = height;
|
|
74
|
+
_surface = nullptr;
|
|
75
|
+
|
|
76
|
+
__android_log_print(ANDROID_LOG_INFO, "GameRenderer",
|
|
77
|
+
"onSurfaceChanged: Width: %d, Height: %d", _width,
|
|
78
|
+
_height);
|
|
79
|
+
});
|
|
73
80
|
}
|
|
74
81
|
|
|
75
82
|
void GameRenderer::onSurfaceDestroyed() {
|
|
76
|
-
|
|
77
|
-
|
|
83
|
+
std::lock_guard<std::mutex> lock(_taskMutex);
|
|
84
|
+
_taskQueue.push([this]() {
|
|
85
|
+
if (_eglDisplay != EGL_NO_DISPLAY) {
|
|
86
|
+
eglMakeCurrent(_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
|
87
|
+
EGL_NO_CONTEXT);
|
|
88
|
+
|
|
89
|
+
if (_eglSurface != EGL_NO_SURFACE) {
|
|
90
|
+
eglDestroySurface(_eglDisplay, _eglSurface);
|
|
91
|
+
_eglSurface = EGL_NO_SURFACE;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
78
94
|
|
|
79
|
-
if (
|
|
80
|
-
|
|
81
|
-
|
|
95
|
+
if (_nativeWindow) {
|
|
96
|
+
ANativeWindow_release(_nativeWindow);
|
|
97
|
+
_nativeWindow = nullptr;
|
|
82
98
|
}
|
|
83
|
-
}
|
|
84
99
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
100
|
+
_surface = nullptr;
|
|
101
|
+
_width = 0;
|
|
102
|
+
_height = 0;
|
|
103
|
+
|
|
104
|
+
__android_log_print(ANDROID_LOG_INFO, "GameRenderer", "onSurfaceDestroyed");
|
|
105
|
+
});
|
|
106
|
+
}
|
|
89
107
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
_height = 0;
|
|
108
|
+
void GameRenderer::processPendingTasks() {
|
|
109
|
+
std::queue<std::function<void()>> tasksToProcess;
|
|
93
110
|
|
|
94
|
-
|
|
111
|
+
{
|
|
112
|
+
std::lock_guard<std::mutex> lock(_taskMutex);
|
|
113
|
+
std::swap(tasksToProcess, _taskQueue);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
while (!tasksToProcess.empty()) {
|
|
117
|
+
tasksToProcess.front()(); // Executes on the game loop thread
|
|
118
|
+
tasksToProcess.pop();
|
|
119
|
+
}
|
|
95
120
|
}
|
|
96
121
|
|
|
97
122
|
void GameRenderer::render(const Screen &screen,
|
|
98
123
|
const std::map<std::string, Entity> &entities) {
|
|
99
124
|
|
|
100
|
-
__android_log_print(ANDROID_LOG_DEBUG, "GameRenderer",
|
|
101
|
-
"render: called, entities=%zu", entities.size());
|
|
102
|
-
|
|
103
125
|
if (!_grContext || _eglSurface == EGL_NO_SURFACE || _width == 0 ||
|
|
104
126
|
_height == 0) {
|
|
105
127
|
__android_log_print(ANDROID_LOG_WARN, "GameRenderer",
|
|
@@ -107,12 +129,6 @@ void GameRenderer::render(const Screen &screen,
|
|
|
107
129
|
return;
|
|
108
130
|
}
|
|
109
131
|
|
|
110
|
-
if (!eglMakeCurrent(_eglDisplay, _eglSurface, _eglSurface, _eglContext)) {
|
|
111
|
-
__android_log_print(ANDROID_LOG_ERROR, "GameRenderer",
|
|
112
|
-
"render: eglMakeCurrent failed: 0x%x", eglGetError());
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
132
|
if (!_surface) {
|
|
117
133
|
if (!createSkSurface()) {
|
|
118
134
|
return;
|
|
@@ -228,8 +244,6 @@ void GameRenderer::render(const Screen &screen,
|
|
|
228
244
|
|
|
229
245
|
_grContext->flushAndSubmit(_surface.get());
|
|
230
246
|
eglSwapBuffers(_eglDisplay, _eglSurface);
|
|
231
|
-
|
|
232
|
-
__android_log_print(ANDROID_LOG_DEBUG, "GameRenderer", "render: complete");
|
|
233
247
|
}
|
|
234
248
|
|
|
235
249
|
bool GameRenderer::isEntityVisible(const Entity &entity,
|
|
@@ -342,8 +356,6 @@ bool GameRenderer::createEGLSurface() {
|
|
|
342
356
|
}
|
|
343
357
|
}
|
|
344
358
|
|
|
345
|
-
eglMakeCurrent(_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
|
346
|
-
|
|
347
359
|
__android_log_print(ANDROID_LOG_INFO, "GameRenderer",
|
|
348
360
|
"createEGLSurface: EGL surface created");
|
|
349
361
|
return true;
|
package/shared/GameRenderer.hpp
CHANGED
|
@@ -36,6 +36,7 @@ public:
|
|
|
36
36
|
void onSurfaceChanged(int width, int height);
|
|
37
37
|
void onSurfaceDestroyed();
|
|
38
38
|
|
|
39
|
+
void processPendingTasks();
|
|
39
40
|
void render(const Screen &screen,
|
|
40
41
|
const std::map<std::string, Entity> &entities);
|
|
41
42
|
|
|
@@ -59,6 +60,9 @@ private:
|
|
|
59
60
|
std::map<double, sk_sp<skottie::Animation>> _lottieCache;
|
|
60
61
|
std::map<double, std::variant<sk_sp<SkSVGDOM>, sk_sp<SkImage>>> _imageCache;
|
|
61
62
|
|
|
63
|
+
std::mutex _taskMutex;
|
|
64
|
+
std::queue<std::function<void()>> _taskQueue;
|
|
65
|
+
|
|
62
66
|
bool isEntityVisible(const Entity &entity, const SkRect &viewBounds);
|
|
63
67
|
|
|
64
68
|
bool initializeEGL();
|
package/android/kls_database.db
DELETED
|
Binary file
|