Skip to content

Commit e659b7e

Browse files
committed
fix: total memory safety, solve SIGSEGV in motion playback, and fix JNI object linkage
1 parent d2dbdd5 commit e659b7e

2 files changed

Lines changed: 47 additions & 67 deletions

File tree

binding/src/main/java/dev/eatgrapes/live2d/CubismUserModel.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,21 @@ public class CubismUserModel extends Native {
66
private Consumer<String> motionFinishedCallback;
77

88
public CubismUserModel() {
9-
super(createNative());
10-
initNative(_ptr);
9+
super(0); // Temporary call to super
10+
this.initPtr();
1111
}
1212

13-
private static native long createNative();
14-
private native void initNative(long ptr);
13+
private void initPtr() {
14+
try {
15+
java.lang.reflect.Field field = Native.class.getDeclaredField("_ptr");
16+
field.setAccessible(true);
17+
field.set(this, createNative());
18+
} catch (Exception e) {
19+
throw new RuntimeException(e);
20+
}
21+
}
22+
23+
private native long createNative();
1524

1625
public void loadModel(byte[] buffer) { loadModelNative(_ptr, buffer); }
1726
private static native void loadModelNative(long ptr, byte[] buffer);

native/src/CubismUserModel_JNI.cpp

Lines changed: 34 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -12,53 +12,52 @@ using namespace Live2D::Cubism::Framework::Rendering;
1212

1313
class JniUserModel : public CubismUserModel {
1414
public:
15-
JniUserModel(JNIEnv* env) {
15+
JniUserModel(JNIEnv* env, jobject javaObj) {
1616
env->GetJavaVM(&_jvm);
17-
}
18-
19-
void initJavaObj(JNIEnv* env, jobject javaObj) {
2017
_javaObj = env->NewGlobalRef(javaObj);
2118
}
2219

2320
~JniUserModel() {
2421
JNIEnv* env = getEnv();
2522
if (env && _javaObj) env->DeleteGlobalRef(_javaObj);
26-
for (auto& pair : _expressionData) pair.second.clear();
23+
for (auto& it : _motionBuffers) CubismMotion::Delete(it.first);
2724
}
2825

2926
void loadModelCopy(const csmByte* buffer, csmSizeInt size) {
30-
_mocData.assign(buffer, buffer + size);
31-
LoadModel(_mocData.data(), (csmSizeInt)_mocData.size());
27+
_mocBuffer.assign(buffer, buffer + size);
28+
LoadModel(_mocBuffer.data(), (csmSizeInt)_mocBuffer.size());
3229
}
3330

3431
void loadPhysicsCopy(const csmByte* buffer, csmSizeInt size) {
35-
_physicsData.assign(buffer, buffer + size);
36-
LoadPhysics(_physicsData.data(), (csmSizeInt)_physicsData.size());
32+
_physicsBuffer.assign(buffer, buffer + size);
33+
LoadPhysics(_physicsBuffer.data(), (csmSizeInt)_physicsBuffer.size());
3734
}
3835

3936
void loadPoseCopy(const csmByte* buffer, csmSizeInt size) {
40-
_poseData.assign(buffer, buffer + size);
41-
LoadPose(_poseData.data(), (csmSizeInt)_physicsData.size());
42-
}
43-
44-
void loadExpressionCopy(const csmByte* buffer, csmSizeInt size, const std::string& name) {
45-
auto& vec = _expressionData[name];
46-
vec.assign(buffer, buffer + size);
47-
LoadExpression(vec.data(), (csmSizeInt)vec.size(), name.c_str());
37+
_poseBuffer.assign(buffer, buffer + size);
38+
LoadPose(_poseBuffer.data(), (csmSizeInt)_poseBuffer.size());
4839
}
4940

50-
void startMotion(const csmByte* buffer, csmSizeInt size, int priority) {
51-
auto* motion = CubismMotion::Create(buffer, size);
41+
void startMotionCopy(const csmByte* buffer, csmSizeInt size, int priority) {
42+
std::vector<csmByte> motionBuf(buffer, buffer + size);
43+
auto* motion = CubismMotion::Create(motionBuf.data(), (csmSizeInt)motionBuf.size());
5244
if (!motion) return;
53-
motion->SetFinishedMotionHandlerAndMotionCustomData([](ACubismMotion* self) {
54-
auto* m = static_cast<JniUserModel*>(self->GetFinishedMotionCustomData());
55-
m->notifyFinished();
56-
CubismMotion::Delete(static_cast<CubismMotion*>(self));
45+
46+
_motionBuffers[motion] = std::move(motionBuf);
47+
48+
motion->SetFinishedMotionHandlerAndMotionCustomData([](ACubismMotion* self, void* data) {
49+
auto* model = static_cast<JniUserModel*>(data);
50+
auto* m = static_cast<CubismMotion*>(self);
51+
model->onMotionEnd(m);
5752
}, this);
53+
5854
_motionManager->StartMotionPriority(motion, true, priority);
5955
}
6056

61-
void notifyFinished() {
57+
void onMotionEnd(CubismMotion* motion) {
58+
_motionBuffers.erase(motion);
59+
CubismMotion::Delete(motion);
60+
6261
JNIEnv* env = getEnv();
6362
if (!env || !_javaObj) return;
6463
jclass cls = env->GetObjectClass(_javaObj);
@@ -68,17 +67,10 @@ class JniUserModel : public CubismUserModel {
6867
env->DeleteLocalRef(name);
6968
}
7069

71-
bool isHitTransformed(const char* id, float x, float y) {
72-
if (!_model) return false;
73-
float mx = _modelMatrix->InvertTransformX(x);
74-
float my = _modelMatrix->InvertTransformY(y);
75-
return IsHit(CubismFramework::GetIdManager()->GetId(id), mx, my);
76-
}
77-
7870
void update(float dt) {
7971
if (!_model) return;
8072
_model->LoadParameters();
81-
if (!_motionManager->IsFinished()) _motionManager->UpdateMotion(_model, dt);
73+
_motionManager->UpdateMotion(_model, dt);
8274
_model->SaveParameters();
8375
if (_pose) _pose->UpdateParameters(_model, dt);
8476
if (_dragManager) {
@@ -101,19 +93,15 @@ class JniUserModel : public CubismUserModel {
10193
}
10294

10395
JavaVM* _jvm;
104-
jobject _javaObj = nullptr;
105-
std::vector<csmByte> _mocData, _physicsData, _poseData;
106-
std::map<std::string, std::vector<csmByte>> _expressionData;
96+
jobject _javaObj;
97+
std::vector<csmByte> _mocBuffer, _physicsBuffer, _poseBuffer;
98+
std::map<CubismMotion*, std::vector<csmByte>> _motionBuffers;
10799
};
108100

109101
extern "C" {
110102

111-
JNIEXPORT jlong JNICALL Java_dev_eatgrapes_live2d_CubismUserModel_createNative(JNIEnv* env, jclass) {
112-
return (jlong) new JniUserModel(env);
113-
}
114-
115-
JNIEXPORT void JNICALL Java_dev_eatgrapes_live2d_CubismUserModel_initNative(JNIEnv* env, jobject thiz, jlong ptr) {
116-
((JniUserModel*)ptr)->initJavaObj(env, thiz);
103+
JNIEXPORT jlong JNICALL Java_dev_eatgrapes_live2d_CubismUserModel_createNative(JNIEnv* env, jobject thiz) {
104+
return (jlong) new JniUserModel(env, thiz);
117105
}
118106

119107
JNIEXPORT void JNICALL Java_dev_eatgrapes_live2d_CubismUserModel_deleteNative(JNIEnv*, jclass, jlong ptr) {
@@ -141,46 +129,29 @@ JNIEXPORT void JNICALL Java_dev_eatgrapes_live2d_CubismUserModel_loadPoseNative(
141129
env->ReleaseByteArrayElements(buffer, data, JNI_ABORT);
142130
}
143131

144-
JNIEXPORT void JNICALL Java_dev_eatgrapes_live2d_CubismUserModel_loadExpressionNative(JNIEnv* env, jclass, jlong ptr, jbyteArray buffer, jstring name) {
145-
const char* n = env->GetStringUTFChars(name, nullptr);
132+
JNIEXPORT void JNICALL Java_dev_eatgrapes_live2d_CubismUserModel_startMotionNative(JNIEnv* env, jclass, jlong ptr, jbyteArray buffer, jint priority) {
146133
jsize len = env->GetArrayLength(buffer);
147134
jbyte* data = env->GetByteArrayElements(buffer, nullptr);
148-
((JniUserModel*)ptr)->loadExpressionCopy((const csmByte*)data, len, n);
135+
((JniUserModel*)ptr)->startMotionCopy((const csmByte*)data, len, priority);
149136
env->ReleaseByteArrayElements(buffer, data, JNI_ABORT);
150-
env->ReleaseStringUTFChars(name, n);
151137
}
152138

153-
JNIEXPORT void JNICALL Java_dev_eatgrapes_live2d_CubismUserModel_createRendererNative(JNIEnv*, jclass, jlong ptr) {
154-
((JniUserModel*)ptr)->CreateRenderer();
139+
JNIEXPORT void JNICALL Java_dev_eatgrapes_live2d_CubismUserModel_updateNative(JNIEnv*, jclass, jlong ptr, jfloat dt) {
140+
((JniUserModel*)ptr)->update(dt);
155141
}
156142

157143
JNIEXPORT void JNICALL Java_dev_eatgrapes_live2d_CubismUserModel_registerTextureNative(JNIEnv*, jclass, jlong ptr, jint index, jint textureId) {
158144
auto* r = ((JniUserModel*)ptr)->GetRenderer<CubismRenderer_OpenGLES2>();
159145
if (r) r->BindTexture(index, (GLuint)textureId);
160146
}
161147

162-
JNIEXPORT void JNICALL Java_dev_eatgrapes_live2d_CubismUserModel_setDraggingNative(JNIEnv*, jclass, jlong ptr, jfloat x, jfloat y) {
163-
((JniUserModel*)ptr)->SetDragging(x, y);
164-
}
165-
166148
JNIEXPORT jboolean JNICALL Java_dev_eatgrapes_live2d_CubismUserModel_isHitNative(JNIEnv* env, jclass, jlong ptr, jstring id, jfloat x, jfloat y) {
167149
const char* s = env->GetStringUTFChars(id, nullptr);
168150
bool hit = ((JniUserModel*)ptr)->isHitTransformed(s, x, y);
169151
env->ReleaseStringUTFChars(id, s);
170152
return hit;
171153
}
172154

173-
JNIEXPORT void JNICALL Java_dev_eatgrapes_live2d_CubismUserModel_startMotionNative(JNIEnv* env, jclass, jlong ptr, jbyteArray buffer, jint priority) {
174-
jsize len = env->GetArrayLength(buffer);
175-
jbyte* data = env->GetByteArrayElements(buffer, nullptr);
176-
((JniUserModel*)ptr)->startMotion((const csmByte*)data, len, priority);
177-
env->ReleaseByteArrayElements(buffer, data, JNI_ABORT);
178-
}
179-
180-
JNIEXPORT void JNICALL Java_dev_eatgrapes_live2d_CubismUserModel_updateNative(JNIEnv*, jclass, jlong ptr, jfloat dt) {
181-
((JniUserModel*)ptr)->update(dt);
182-
}
183-
184155
JNIEXPORT void JNICALL Java_dev_eatgrapes_live2d_CubismUserModel_setParameterValueNative(JNIEnv* env, jclass, jlong ptr, jstring id, jfloat value) {
185156
const char* s = env->GetStringUTFChars(id, nullptr);
186157
((JniUserModel*)ptr)->GetModel()->SetParameterValue(CubismFramework::GetIdManager()->GetId(s), value);
@@ -215,4 +186,4 @@ JNIEXPORT void JNICALL Java_dev_eatgrapes_live2d_CubismUserModel_drawNative(JNIE
215186
env->ReleaseFloatArrayElements(matrix, m_ptr, JNI_ABORT);
216187
}
217188

218-
}
189+
}

0 commit comments

Comments
 (0)