@@ -18,6 +18,7 @@ use jni::sys::{jboolean, jdouble, jint, jlong};
1818use jni:: { JNIEnv , JavaVM } ;
1919use rapier3d:: glamx:: { DVec3 , Quat } ;
2020use std:: collections:: HashMap ;
21+ use std:: io:: { self , Write } ;
2122use std:: sync:: { Arc , OnceLock , RwLock } ;
2223
2324use fern:: colors:: { Color , ColoredLevelConfig } ;
@@ -303,122 +304,166 @@ pub fn get_rigid_body<'a>(
303304 & sim. rigid_body_set [ * handle]
304305}
305306
307+ /// Stdout/stderr attached to CurseForge/Overwolf become broken pipes when the launcher is closed
308+ /// while Minecraft stays open. fern → log then panics with os error 232, aborting initialize even
309+ /// though the physics scene was already constructed. Treat closed-pipe writes as success.
310+ struct IgnoreClosedPipe < W > ( W ) ;
311+
312+ impl < W : Write > Write for IgnoreClosedPipe < W > {
313+ fn write ( & mut self , buf : & [ u8 ] ) -> io:: Result < usize > {
314+ match self . 0 . write ( buf) {
315+ Err ( e) if is_closed_pipe ( & e) => Ok ( buf. len ( ) ) ,
316+ other => other,
317+ }
318+ }
319+
320+ fn flush ( & mut self ) -> io:: Result < ( ) > {
321+ match self . 0 . flush ( ) {
322+ Err ( e) if is_closed_pipe ( & e) => Ok ( ( ) ) ,
323+ other => other,
324+ }
325+ }
326+ }
327+
328+ fn is_closed_pipe ( err : & io:: Error ) -> bool {
329+ matches ! (
330+ err. kind( ) ,
331+ io:: ErrorKind :: BrokenPipe | io:: ErrorKind :: UnexpectedEof
332+ ) || err. raw_os_error ( ) == Some ( 232 ) // Windows ERROR_NO_DATA / "The pipe is being closed"
333+ }
334+
306335#[ unsafe( no_mangle) ]
307336pub extern "system" fn Java_dev_ryanhcode_sable_physics_impl_rapier_Rapier3D_initialize < ' local > (
308- env : JNIEnv < ' local > ,
337+ mut env : JNIEnv < ' local > ,
309338 _class : JClass < ' local > ,
310339 x : jdouble ,
311340 y : jdouble ,
312341 z : jdouble ,
313342 universal_drag : jdouble ,
314343) -> jlong {
315- PHYSICS_STATE . get_or_init ( || {
316- let colors = ColoredLevelConfig :: new ( )
317- . info ( Color :: Green )
318- . error ( Color :: Red )
319- . debug ( Color :: Blue ) ;
320-
321- let _ = fern:: Dispatch :: new ( )
322- . format ( move |out, message, record| {
323- out. finish ( format_args ! (
324- "[{}] [{}] ({}) {}" ,
325- humantime:: format_rfc3339( std:: time:: SystemTime :: now( ) ) ,
326- colors. color( record. level( ) ) ,
327- record. target( ) ,
328- message
329- ) )
344+ // initialize used to unwind across the JNI boundary on panic, which hard-kills the JVM
345+ // (EXCEPTION_UNCAUGHT_CXX_EXCEPTION / hs_err) with no Minecraft crash report. Mirror tick/step.
346+ let result = std:: panic:: catch_unwind ( std:: panic:: AssertUnwindSafe ( || {
347+ PHYSICS_STATE . get_or_init ( || {
348+ let colors = ColoredLevelConfig :: new ( )
349+ . info ( Color :: Green )
350+ . error ( Color :: Red )
351+ . debug ( Color :: Blue ) ;
352+
353+ let _ = fern:: Dispatch :: new ( )
354+ . format ( move |out, message, record| {
355+ out. finish ( format_args ! (
356+ "[{}] [{}] ({}) {}" ,
357+ humantime:: format_rfc3339( std:: time:: SystemTime :: now( ) ) ,
358+ colors. color( record. level( ) ) ,
359+ record. target( ) ,
360+ message
361+ ) )
362+ } )
363+ . level ( log:: LevelFilter :: Info )
364+ . level_for ( "jni" , log:: LevelFilter :: Error )
365+ . chain ( Box :: new ( IgnoreClosedPipe ( io:: stdout ( ) ) ) as Box < dyn Write + Send > )
366+ . apply ( ) ;
367+
368+ RwLock :: new ( PhysicsState {
369+ integration_parameters : IntegrationParameters {
370+ dt : 1.0 / 20.0 ,
371+
372+ max_ccd_substeps : 3 ,
373+ normalized_prediction_distance : 0.005 ,
374+
375+ contact_softness : SpringCoefficients {
376+ natural_frequency : 30.0 ,
377+ damping_ratio : 5.0 ,
378+ } ,
379+
380+ normalized_max_corrective_velocity : 50.0 ,
381+ normalized_allowed_linear_error : 0.0025 ,
382+
383+ ..IntegrationParameters :: default ( )
384+ } ,
385+ voxel_collider_map : VoxelColliderMap :: new ( ) ,
330386 } )
331- . level ( log:: LevelFilter :: Info )
332- . level_for ( "jni" , log:: LevelFilter :: Error )
333- . chain ( std:: io:: stdout ( ) )
334- . apply ( ) ;
335-
336- RwLock :: new ( PhysicsState {
337- integration_parameters : IntegrationParameters {
338- dt : 1.0 / 20.0 ,
387+ } ) ;
339388
340- max_ccd_substeps : 3 ,
341- normalized_prediction_distance : 0.005 ,
389+ let ground = RigidBodyBuilder :: fixed ( ) ;
390+
391+ let collider = ColliderBuilder :: new ( SharedShape :: new ( LevelCollider :: new ( None , true ) ) )
392+ . collision_groups ( LEVEL_GROUP )
393+ . build ( ) ;
394+
395+ let sable_data = Arc :: new ( RwLock :: new ( SableSceneData {
396+ main_level_chunks : HashMap :: < i64 , ChunkSection > :: new ( ) ,
397+ octree_chunks : HashMap :: < i64 , OctreeChunkSection > :: new ( ) ,
398+ joint_set : SableJointSet :: new ( ) ,
399+ rope_map : RopeMap :: default ( ) ,
400+ level_colliders : HashMap :: < LevelColliderID , ActiveLevelColliderInfo > :: new ( ) ,
401+ rigid_bodies : HashMap :: < LevelColliderID , RigidBodyHandle > :: new ( ) ,
402+ } ) ) ;
403+ let manifold_info_map = Arc :: new ( SableManifoldInfoMap :: default ( ) ) ;
404+ let reported_collisions = Arc :: new ( ReportedCollisionBuffer :: new ( ) ) ;
405+ let current_step_vm = Some ( Arc :: new ( unsafe {
406+ JavaVM :: from_raw ( env. get_java_vm ( ) . unwrap ( ) . get_java_vm_pointer ( ) ) . unwrap ( )
407+ } ) ) ;
408+
409+ let dispatcher = SableDispatcher {
410+ sable_data : Arc :: clone ( & sable_data) ,
411+ manifold_info_map : Arc :: clone ( & manifold_info_map) ,
412+ } ;
342413
343- contact_softness : SpringCoefficients {
344- natural_frequency : 30.0 ,
345- damping_ratio : 5.0 ,
414+ let mut scene = PhysicsScene {
415+ sim_data : RwLock :: new ( SimulationSceneData {
416+ pipeline : PhysicsPipeline :: new ( ) ,
417+ rigid_body_set : RigidBodySet :: new ( ) ,
418+ collider_set : ColliderSet :: new ( ) ,
419+ island_manager : IslandManager :: new ( ) ,
420+ broad_phase : DefaultBroadPhase :: new ( ) ,
421+ narrow_phase : NarrowPhase :: with_query_dispatcher (
422+ dispatcher. chain ( DefaultQueryDispatcher ) ,
423+ ) ,
424+ impulse_joint_set : ImpulseJointSet :: new ( ) ,
425+ multibody_joint_set : MultibodyJointSet :: new ( ) ,
426+ ccd_solver : CCDSolver :: new ( ) ,
427+ physics_hooks : SablePhysicsHooks {
428+ sable_data : Arc :: clone ( & sable_data) ,
429+ manifold_info_map : Arc :: clone ( & manifold_info_map) ,
430+ current_step_vm : current_step_vm. clone ( ) ,
346431 } ,
432+ event_handler : SableEventHandler {
433+ reported_collisions : Arc :: clone ( & reported_collisions) ,
434+ } ,
435+ } ) ,
436+ sable_data,
437+ ground_handle : None ,
438+ reported_collisions,
439+ current_step_vm,
440+ gravity : Vec3 :: new ( x as Real , y as Real , z as Real ) ,
441+ universal_drag : universal_drag as Real ,
442+ manifold_info_map,
443+ } ;
347444
348- normalized_max_corrective_velocity : 50.0 ,
349- normalized_allowed_linear_error : 0.0025 ,
350-
351- ..IntegrationParameters :: default ( )
352- } ,
353- voxel_collider_map : VoxelColliderMap :: new ( ) ,
354- } )
355- } ) ;
356-
357- let ground = RigidBodyBuilder :: fixed ( ) ;
445+ {
446+ let mut sim_data = scene. sim_data . write ( ) . unwrap ( ) ;
447+ sim_data. collider_set . insert ( collider) ;
358448
359- let collider = ColliderBuilder :: new ( SharedShape :: new ( LevelCollider :: new ( None , true ) ) )
360- . collision_groups ( LEVEL_GROUP )
361- . build ( ) ;
449+ scene. ground_handle = Some ( sim_data. rigid_body_set . insert ( ground) ) ;
450+ }
362451
363- let sable_data = Arc :: new ( RwLock :: new ( SableSceneData {
364- main_level_chunks : HashMap :: < i64 , ChunkSection > :: new ( ) ,
365- octree_chunks : HashMap :: < i64 , OctreeChunkSection > :: new ( ) ,
366- joint_set : SableJointSet :: new ( ) ,
367- rope_map : RopeMap :: default ( ) ,
368- level_colliders : HashMap :: < LevelColliderID , ActiveLevelColliderInfo > :: new ( ) ,
369- rigid_bodies : HashMap :: < LevelColliderID , RigidBodyHandle > :: new ( ) ,
370- } ) ) ;
371- let manifold_info_map = Arc :: new ( SableManifoldInfoMap :: default ( ) ) ;
372- let reported_collisions = Arc :: new ( ReportedCollisionBuffer :: new ( ) ) ;
373- let current_step_vm = Some ( Arc :: new ( unsafe {
374- JavaVM :: from_raw ( env. get_java_vm ( ) . unwrap ( ) . get_java_vm_pointer ( ) ) . unwrap ( )
452+ info ! ( "Rapier scene initialized" ) ;
453+ Arc :: into_raw ( Arc :: new ( scene) ) as jlong
375454 } ) ) ;
376455
377- let dispatcher = SableDispatcher {
378- sable_data : Arc :: clone ( & sable_data) ,
379- manifold_info_map : Arc :: clone ( & manifold_info_map) ,
380- } ;
381-
382- let mut scene = PhysicsScene {
383- sim_data : RwLock :: new ( SimulationSceneData {
384- pipeline : PhysicsPipeline :: new ( ) ,
385- rigid_body_set : RigidBodySet :: new ( ) ,
386- collider_set : ColliderSet :: new ( ) ,
387- island_manager : IslandManager :: new ( ) ,
388- broad_phase : DefaultBroadPhase :: new ( ) ,
389- narrow_phase : NarrowPhase :: with_query_dispatcher (
390- dispatcher. chain ( DefaultQueryDispatcher ) ,
391- ) ,
392- impulse_joint_set : ImpulseJointSet :: new ( ) ,
393- multibody_joint_set : MultibodyJointSet :: new ( ) ,
394- ccd_solver : CCDSolver :: new ( ) ,
395- physics_hooks : SablePhysicsHooks {
396- sable_data : Arc :: clone ( & sable_data) ,
397- manifold_info_map : Arc :: clone ( & manifold_info_map) ,
398- current_step_vm : current_step_vm. clone ( ) ,
399- } ,
400- event_handler : SableEventHandler {
401- reported_collisions : Arc :: clone ( & reported_collisions) ,
402- } ,
403- } ) ,
404- sable_data,
405- ground_handle : None ,
406- reported_collisions,
407- current_step_vm,
408- gravity : Vec3 :: new ( x as Real , y as Real , z as Real ) ,
409- universal_drag : universal_drag as Real ,
410- manifold_info_map,
411- } ;
412-
413- {
414- let mut sim_data = scene. sim_data . write ( ) . unwrap ( ) ;
415- sim_data. collider_set . insert ( collider) ;
416-
417- scene. ground_handle = Some ( sim_data. rigid_body_set . insert ( ground) ) ;
456+ match result {
457+ Ok ( handle) => handle,
458+ Err ( payload) => {
459+ let msg = format ! (
460+ "Rapier native panic during initialize: {}" ,
461+ panic_message( & payload)
462+ ) ;
463+ let _ = env. throw_new ( "java/lang/RuntimeException" , & msg) ;
464+ 0
465+ }
418466 }
419-
420- info ! ( "Rapier scene initialized" ) ;
421- Arc :: into_raw ( Arc :: new ( scene) ) as jlong
422467}
423468
424469#[ unsafe( no_mangle) ]
0 commit comments