2626import com .loohp .interactionvisualizer .debug .PerformanceBlockScene ;
2727import com .loohp .interactionvisualizer .debug .PerformanceScene ;
2828import com .loohp .interactionvisualizer .integration .CustomContentManager ;
29+ import com .loohp .interactionvisualizer .integration .ViewerCullingManager ;
30+ import com .loohp .interactionvisualizer .integration .ViewerCullingManagerLoader ;
2931import com .loohp .interactionvisualizer .managers .AsyncExecutorManager ;
3032import com .loohp .interactionvisualizer .managers .LangManager ;
3133import com .loohp .interactionvisualizer .managers .LightManager ;
@@ -75,10 +77,15 @@ public class InteractionVisualizer extends JavaPlugin {
7577 public static final int BSTATS_PLUGIN_ID = 7024 ;
7678 public static final String CONFIG_ID = "config" ;
7779 public static final Set <String > SUPPORTED_MINECRAFT_VERSIONS = Set .of ("26.1.2" , "26.2" );
80+ private static final String PERFORMANCE_MIGRATION_MARKER =
81+ ".paper26-performance-defaults-advised" ;
7882
7983 public static InteractionVisualizer plugin = null ;
8084
85+ /** @deprecated LightAPI is no longer used; retained for binary compatibility. */
86+ @ Deprecated (forRemoval = false )
8187 public static Boolean lightapi = false ;
88+ public static Boolean craftEngineLight = false ;
8289 public static Boolean openinv = false ;
8390
8491 public static Set <String > exemptBlocks = new HashSet <>();
@@ -109,20 +116,25 @@ public class InteractionVisualizer extends JavaPlugin {
109116
110117 public static boolean defaultDisabledAll = false ;
111118 /** A/B switch: virtual item remains authoritative while an invisible tracker stays stationary. */
112- public static boolean staticVirtualItemAnchorsDuringAnimation = false ;
119+ public static boolean staticVirtualItemAnchorsDuringAnimation = true ;
113120 /** A/B switch: eligible stationary virtual items are tracked and rendered entirely by packets. */
114- public static boolean packetOnlyStaticVirtualItems = false ;
121+ public static boolean packetOnlyStaticVirtualItems = true ;
122+ /** A/B switch: safe animated virtual items use Typewriter-style per-viewer packet tracking. */
123+ public static boolean packetOnlyAnimatedVirtualItems = false ;
115124 /** A/B switch: smooths visibility recovery bursts; hides are always immediate. */
116- public static boolean visibilityRateLimiting = false ;
125+ public static boolean visibilityRateLimiting = true ;
117126 public static int visibilityRateLimitBucketSize = 128 ;
118127 public static int visibilityRateLimitRestorePerTick = 32 ;
119128 /** A/B switch: coalesces block changes and updates tracked blocks from fixed-budget loops. */
120- public static boolean eventDrivenBlockUpdates = false ;
129+ public static boolean eventDrivenBlockUpdates = true ;
121130 public static int blockUpdateMaxDirtyPerTick = 64 ;
122131
123132 private boolean blockUpdateModeInitialized ;
133+ private boolean cullingLifecycleInitialized ;
134+ private boolean cullingProviderWarningLogged ;
124135
125136 public static ILightManager lightManager ;
137+ public static ViewerCullingManager viewerCullingManager = ViewerCullingManager .DISABLED ;
126138 public static PreferenceManager preferenceManager ;
127139 public static AsyncExecutorManager asyncExecutorManager ;
128140
@@ -160,18 +172,9 @@ public void onEnable() {
160172 new ThreadPoolExecutor .CallerRunsPolicy ());
161173 asyncExecutorManager = new AsyncExecutorManager (threadPool );
162174
163- if (isPluginEnabled ("LightAPI" )) {
164- try {
165- Class .forName ("ru.beykerykt.lightapi.utils.Debug" );
166- hookMessage ("LightAPI" );
167- lightapi = true ;
168- lightManager = new LightManager (this );
169- } catch (ClassNotFoundException ignored ) {
170- }
171- }
172- if (!lightapi ) {
173- lightManager = ILightManager .DUMMY_INSTANCE ;
174- }
175+ lightapi = false ;
176+ craftEngineLight = false ;
177+ lightManager = ILightManager .DUMMY_INSTANCE ;
175178 if (isPluginEnabled ("OpenInv" )) {
176179 hookMessage ("OpenInv" );
177180 openinv = true ;
@@ -180,16 +183,32 @@ public void onEnable() {
180183 if (!getDataFolder ().exists ()) {
181184 getDataFolder ().mkdirs ();
182185 }
186+ File configFile = new File (getDataFolder (), "config.yml" );
187+ boolean existingConfiguration = configFile .isFile ();
183188 try {
184- Config .loadConfig (CONFIG_ID , new File ( getDataFolder (), "config.yml" ) , getClass ().getClassLoader ().getResourceAsStream ("config.yml" ), getClass ().getClassLoader ().getResourceAsStream ("config.yml" ), true );
189+ Config .loadConfig (CONFIG_ID , configFile , getClass ().getClassLoader ().getResourceAsStream ("config.yml" ), getClass ().getClassLoader ().getResourceAsStream ("config.yml" ), true );
185190 } catch (IOException e ) {
186191 e .printStackTrace ();
187192 getServer ().getPluginManager ().disablePlugin (this );
188193 return ;
189194 }
190195 loadConfig ();
191-
192- if (CustomContentManager .initialize (this ).contains ("craftengine" )) {
196+ advisePerformanceMigrationOnce (existingConfiguration );
197+
198+ boolean craftEngineHooked = CustomContentManager .initialize (this ).contains ("craftengine" );
199+ if (isPluginEnabled ("CraftEngine" )) {
200+ ILightManager craftEngineManager = LightManager .createCraftEngine (this , lightUpdatePeriod ).orElse (null );
201+ if (craftEngineManager != null ) {
202+ lightManager = craftEngineManager ;
203+ craftEngineLight = true ;
204+ craftEngineHooked = true ;
205+ }
206+ }
207+ if (configureViewerCulling ()) {
208+ craftEngineHooked = true ;
209+ }
210+ cullingLifecycleInitialized = true ;
211+ if (craftEngineHooked ) {
193212 hookMessage ("CraftEngine" );
194213 }
195214
@@ -253,18 +272,41 @@ public void onEnable() {
253272 @ Override
254273 public void onDisable () {
255274 shutdownPerformanceScenes ();
275+ int retainedLightState = 0 ;
276+ ILightManager disablingLightManager = lightManager ;
277+ if (disablingLightManager != null ) {
278+ disablingLightManager .shutdown ();
279+ retainedLightState = disablingLightManager .retainedStateCount ();
280+ lightManager = ILightManager .DUMMY_INSTANCE ;
281+ }
282+ ViewerCullingManager disablingCullingManager = viewerCullingManager ;
283+ disablingCullingManager .shutdown ();
284+ int retainedCullingRegistrations = disablingCullingManager .retainedRegistrations ();
285+ viewerCullingManager = ViewerCullingManager .DISABLED ;
256286 CustomContentManager .shutdown ();
257- if (preferenceManager != null ) {
258- preferenceManager .close ();
287+ int retainedPreferenceState = 0 ;
288+ PreferenceManager disablingPreferenceManager = preferenceManager ;
289+ if (disablingPreferenceManager != null ) {
290+ disablingPreferenceManager .close ();
291+ retainedPreferenceState = disablingPreferenceManager .retainedStateCount ();
292+ preferenceManager = null ;
259293 }
294+ Database .close ();
260295 TaskManager .shutdown ();
261296 DisplayManager .shutdown ();
262297 TileEntityManager .shutdown ();
263298 PlayerLocationManager .clearCache ();
299+ playerTrackingRange .clear ();
264300 LegacyTextComponentCache .invalidateAll ();
265- if (asyncExecutorManager != null ) {
266- asyncExecutorManager .close ();
301+ int retainedAsyncTasks = 0 ;
302+ AsyncExecutorManager disablingAsyncExecutorManager = asyncExecutorManager ;
303+ if (disablingAsyncExecutorManager != null ) {
304+ disablingAsyncExecutorManager .close ();
305+ retainedAsyncTasks = disablingAsyncExecutorManager .retainedTaskCount ();
306+ asyncExecutorManager = null ;
267307 }
308+ PerformanceMetrics .logShutdownState (this , retainedAsyncTasks ,
309+ retainedPreferenceState , retainedLightState , retainedCullingRegistrations );
268310 getServer ().getConsoleSender ().sendMessage (Component .text (
269311 "[InteractionVisualizer] Disabled; all display entities removed." , NamedTextColor .RED ));
270312 }
@@ -300,6 +342,43 @@ public SparrowConfiguration getConfiguration() {
300342 return Config .getConfig (CONFIG_ID ).getConfiguration ();
301343 }
302344
345+ private void advisePerformanceMigrationOnce (boolean existingConfiguration ) {
346+ File marker = new File (getDataFolder (), PERFORMANCE_MIGRATION_MARKER );
347+ if (marker .isFile ()) {
348+ return ;
349+ }
350+ try {
351+ if (!marker .createNewFile ()) {
352+ return ;
353+ }
354+ } catch (IOException exception ) {
355+ getLogger ().log (Level .WARNING ,
356+ "Unable to persist the Paper 26 performance migration notice marker" , exception );
357+ }
358+ if (existingConfiguration && hasPreservedPerformanceOptOut ()) {
359+ getLogger ().warning ("Paper 26 performance defaults are enabled for new installations. "
360+ + "Your explicit false values were preserved; review StaticAnchorDuringAnimation, "
361+ + "PacketOnlyStatic, VisibilityRateLimit, BlockUpdates.EventDriven, and dropped-item "
362+ + "VisibilityCulling/VisibilityRateLimit when you are ready to migrate." );
363+ }
364+ }
365+
366+ private boolean hasPreservedPerformanceOptOut () {
367+ SparrowConfiguration configuration = getConfiguration ();
368+ return !configuration .getBoolean (
369+ "Settings.Performance.VirtualItems.StaticAnchorDuringAnimation" )
370+ || !configuration .getBoolean (
371+ "Settings.Performance.VirtualItems.PacketOnlyStatic" )
372+ || !configuration .getBoolean (
373+ "Settings.Performance.VisibilityRateLimit.Enabled" )
374+ || !configuration .getBoolean (
375+ "Settings.Performance.BlockUpdates.EventDriven" )
376+ || !configuration .getBoolean (
377+ "Entities.Item.Options.VisibilityCulling.Enabled" )
378+ || !configuration .getBoolean (
379+ "Entities.Item.Options.VisibilityRateLimit.Enabled" );
380+ }
381+
303382 public void loadConfig () {
304383 Config config = Config .getConfig (CONFIG_ID );
305384 config .reload ();
@@ -326,8 +405,14 @@ public void loadConfig() {
326405
327406 disabledWorlds = new HashSet <>(getConfiguration ().getStringList ("Settings.DisabledWorlds" ));
328407 hideIfObstructed = getConfiguration ().getBoolean ("Settings.HideIfViewObstructed" );
408+ if (cullingLifecycleInitialized ) {
409+ configureViewerCulling ();
410+ }
329411
330412 lightUpdatePeriod = getConfiguration ().getInt ("LightUpdate.Period" );
413+ if (lightManager != null ) {
414+ lightManager .setUpdatePeriod (lightUpdatePeriod );
415+ }
331416
332417 updaterEnabled = getConfiguration ().getBoolean ("Options.Updater" );
333418
@@ -341,6 +426,8 @@ public void loadConfig() {
341426 "Settings.Performance.VirtualItems.StaticAnchorDuringAnimation" );
342427 packetOnlyStaticVirtualItems = getConfiguration ().getBoolean (
343428 "Settings.Performance.VirtualItems.PacketOnlyStatic" );
429+ packetOnlyAnimatedVirtualItems = getConfiguration ().getBoolean (
430+ "Settings.Performance.VirtualItems.PacketOnlyAnimated" );
344431 visibilityRateLimiting = getConfiguration ().getBoolean (
345432 "Settings.Performance.VisibilityRateLimit.Enabled" );
346433 visibilityRateLimitBucketSize = Math .max (1 , getConfiguration ().getInt (
@@ -362,4 +449,46 @@ public void loadConfig() {
362449 getServer ().getPluginManager ().callEvent (new InteractionVisualizerReloadEvent ());
363450 }
364451
452+ private boolean configureViewerCulling () {
453+ if (!hideIfObstructed ) {
454+ boolean backendChanged = viewerCullingManager .enabled ();
455+ viewerCullingManager .shutdown ();
456+ viewerCullingManager = ViewerCullingManager .DISABLED ;
457+ if (backendChanged && cullingLifecycleInitialized ) {
458+ DisplayManager .onCullingBackendChanged ();
459+ }
460+ return false ;
461+ }
462+ if (viewerCullingManager .enabled ()) {
463+ return true ;
464+ }
465+ viewerCullingManager .shutdown ();
466+ viewerCullingManager = ViewerCullingManager .DISABLED ;
467+ if (!isPluginEnabled ("CraftEngine" )) {
468+ if (!cullingProviderWarningLogged ) {
469+ cullingProviderWarningLogged = true ;
470+ getLogger ().warning ("Settings.HideIfViewObstructed requires CraftEngine 26.7+; "
471+ + "using sent-chunk visibility without occlusion culling." );
472+ }
473+ return false ;
474+ }
475+ ViewerCullingManager manager = ViewerCullingManagerLoader .createCraftEngine (
476+ this , DisplayManager ::onCullingVisibility ).orElse (ViewerCullingManager .DISABLED );
477+ if (!manager .enabled ()) {
478+ manager .shutdown ();
479+ if (!cullingProviderWarningLogged ) {
480+ cullingProviderWarningLogged = true ;
481+ getLogger ().warning ("CraftEngine entity culling and ray tracing must both be enabled "
482+ + "for Settings.HideIfViewObstructed; using sent-chunk visibility only." );
483+ }
484+ return false ;
485+ }
486+ cullingProviderWarningLogged = false ;
487+ viewerCullingManager = manager ;
488+ if (cullingLifecycleInitialized ) {
489+ DisplayManager .onCullingBackendChanged ();
490+ }
491+ return true ;
492+ }
493+
365494}
0 commit comments