From 81af854fa5fc2daffa5d466522a2041bd938a3cf Mon Sep 17 00:00:00 2001 From: Frotty Date: Sun, 13 Sep 2026 11:19:20 +0200 Subject: [PATCH] Update optimizer for Reforged 3 natives --- .../optimizer/LocalPlayerContextAnalyzer.java | 21 +++++- .../translation/imoptimizer/ImCompressor.java | 5 +- .../UselessFunctionCallsRemover.java | 14 +++- .../wurstscript/tests/OptimizerTests.java | 69 ++++++++++++++++++- 4 files changed, 104 insertions(+), 5 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/LocalPlayerContextAnalyzer.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/LocalPlayerContextAnalyzer.java index da3eba241..ab76a4aaf 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/LocalPlayerContextAnalyzer.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/intermediatelang/optimizer/LocalPlayerContextAnalyzer.java @@ -51,6 +51,8 @@ public final class LocalPlayerContextAnalyzer { "GetCameraEyePositionY", "GetCameraEyePositionZ", "GetCameraEyePositionLoc", + "GetCameraFieldControlledByInput", + "BlzCameraGetCameraType", // Localized data may vary with the client's language. "GetLocalizedString", @@ -64,7 +66,24 @@ public final class LocalPlayerContextAnalyzer { "BlzGetLocalClientHeight", "BlzIsLocalClientActive", "BlzGetMouseFocusUnit", - "BlzGetLocale" + "BlzGetLocale", + + // Current cinematic and renderer state belongs to the local client. + "BlzGetModelCinematicGameShotCount", + "BlzGetModelCinematicGameCurrentShot", + "BlzGetModelCinematicGameRemainingTime", + "BlzGetMinShadowCastingPointLightCount", + + // Polling input and converting viewport coordinates are client-local. + "BlzIsMetaKeyPressed", + "BlzIsKeyPressed", + "BlzIsMouseButtonPressed", + "BlzGetMouseScreenPosX", + "BlzGetMouseScreenPosY", + "BlzPixelToFrameX", + "BlzPixelToFrameY", + "BlzFrameToPixelX", + "BlzFrameToPixelY" ); private final Set localPlayerDependentVars = diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/ImCompressor.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/ImCompressor.java index 839d1a89f..fb8ae1668 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/ImCompressor.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/ImCompressor.java @@ -22,12 +22,13 @@ public ImCompressor(ImTranslator translator) { this.prog = translator.getImProg(); ng = new NameGenerator(); for (ImVar global : prog.getGlobals()) { - if (NamePreservation.isPreserved(global)) { + if (global.getIsBJ() || NamePreservation.isPreserved(global)) { preservedNames.add(global.getName()); } } for (ImFunction function : ImHelper.calculateFunctionsOfProg(prog)) { - if (NamePreservation.isPreserved(function)) { + if (function.isNative() || function.isBj() || function.isExtern() + || NamePreservation.isPreserved(function)) { preservedNames.add(function.getName()); } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/UselessFunctionCallsRemover.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/UselessFunctionCallsRemover.java index 131424467..f3eae9994 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/UselessFunctionCallsRemover.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imoptimizer/UselessFunctionCallsRemover.java @@ -253,13 +253,25 @@ public static boolean isFunctionPure(String funcName) { "BlzGetItemRealField", "BlzGetItemStringField", "BlzGetUnitBooleanField", "BlzGetUnitIntegerField", "BlzGetUnitRealField", "BlzGetUnitStringField", "BlzGetUnitWeaponBooleanField", "BlzGetUnitWeaponIntegerField", "BlzGetUnitWeaponRealField", "BlzGetUnitWeaponStringField", "BlzGetUnitSkin", "BlzGetItemSkin", - "BlzGetPlayerTownHallCount" + "BlzGetPlayerTownHallCount", + "ConvertFogStyle", "ConvertEquipmentType", "ConvertItemTag", "ConvertLoadoutSlot", + "BlzGetModelCinematicGameShotCount", "BlzGetModelCinematicGameCurrentShot", + "BlzGetModelCinematicGameRemainingTime", "BlzGetMinShadowCastingPointLightCount", + "GetCameraFieldControlledByInput", "BlzCameraGetCameraType", "BlzCameraSetupGetCameraType", + "BlzIsTerrainPathableEx", "BlzGetDoodadX", "BlzGetDoodadY", "BlzGetDoodadZ", + "BlzGetDoodadScaleX", "BlzGetDoodadScaleY", "BlzGetDoodadScaleZ", + "BlzGetDoodadIsUsingModelAxes", "BlzGetDoodadYaw", "BlzGetDoodadPitch", "BlzGetDoodadRoll", + "BlzGetDoodadVariation", "BlzGetDoodadId", "BlzGetNumDoodads", + "BlzGetUnitAbilityCooldownPercent", "BlzIsMetaKeyPressed", "BlzIsKeyPressed", + "BlzIsMouseButtonPressed", "BlzGetMouseScreenPosX", "BlzGetMouseScreenPosY", + "BlzPixelToFrameX", "BlzPixelToFrameY", "BlzFrameToPixelX", "BlzFrameToPixelY" ); private static final Set pureFunctions = HashSet.of( "AbilityId", "AbilityId2String", "Acos", "And", "Asin", "Atan", "Atan2", "ConvertAIDifficulty", "ConvertAllianceType", "ConvertAttackType", "ConvertBlendMode", "ConvertCameraField", "ConvertDamageType", "ConvertDialogEvent", "ConvertEffectType", + "ConvertFogStyle", "ConvertEquipmentType", "ConvertItemTag", "ConvertLoadoutSlot", "ConvertFGameState", "ConvertFogState", "ConvertGameDifficulty", "ConvertGameEvent", "ConvertGameSpeed", "ConvertGameType", "ConvertIGameState", "ConvertItemType", "ConvertLimitOp", "ConvertMapControl", "ConvertMapDensity", "ConvertMapFlag", "ConvertMapSetting", diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/OptimizerTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/OptimizerTests.java index c6dbd10fb..cd48e4c8a 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/OptimizerTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/OptimizerTests.java @@ -15,6 +15,7 @@ import de.peeeq.wurstscript.jassIm.*; import de.peeeq.wurstscript.translation.imoptimizer.ImInliner; import de.peeeq.wurstscript.translation.imoptimizer.ImOptimizer; +import de.peeeq.wurstscript.translation.imoptimizer.UselessFunctionCallsRemover; import de.peeeq.wurstscript.translation.imtranslation.ImTranslator; import de.peeeq.wurstscript.translation.imtranslation.FunctionFlagEnum; import de.peeeq.wurstscript.types.TypesHelper; @@ -661,6 +662,57 @@ public void preservedNamesAreReservedBeforeCompression() throws IOException { "Expected compression to reserve the preserved name.\n" + output); } + @Test + public void nativeNamesAreReservedBeforeCompression() throws IOException { + test().optimize().lines( + "package test", + " native w()", + " function ordinary()", + " w()", + " init", + " ordinary()", + "endpackage"); + + String output = Files.toString( + new File("./test-output/OptimizerTests_nativeNamesAreReservedBeforeCompression_opt.j"), + Charsets.UTF_8); + assertFalse(output.contains("function w takes"), + "Compression must not reuse a native API name.\n" + output); + } + + @Test + public void reforged3ReadOnlyNativesAreClassified() { + java.util.Set readOnlyNatives = java.util.Set.of( + "ConvertFogStyle", "ConvertEquipmentType", "ConvertItemTag", "ConvertLoadoutSlot", + "BlzGetModelCinematicGameShotCount", "BlzGetModelCinematicGameCurrentShot", + "BlzGetModelCinematicGameRemainingTime", "BlzGetMinShadowCastingPointLightCount", + "GetCameraFieldControlledByInput", "BlzCameraGetCameraType", "BlzCameraSetupGetCameraType", + "BlzIsTerrainPathableEx", "BlzGetDoodadX", "BlzGetDoodadY", "BlzGetDoodadZ", + "BlzGetDoodadScaleX", "BlzGetDoodadScaleY", "BlzGetDoodadScaleZ", + "BlzGetDoodadIsUsingModelAxes", "BlzGetDoodadYaw", "BlzGetDoodadPitch", "BlzGetDoodadRoll", + "BlzGetDoodadVariation", "BlzGetDoodadId", "BlzGetNumDoodads", + "BlzGetUnitAbilityCooldownPercent", "BlzIsMetaKeyPressed", "BlzIsKeyPressed", + "BlzIsMouseButtonPressed", "BlzGetMouseScreenPosX", "BlzGetMouseScreenPosY", + "BlzPixelToFrameX", "BlzPixelToFrameY", "BlzFrameToPixelX", "BlzFrameToPixelY" + ); + for (String name : readOnlyNatives) { + assertTrue(UselessFunctionCallsRemover.isFunctionWithoutSideEffect(name), + name + " must be recognized as a side-effect-free Reforged 3 native"); + } + + for (String name : java.util.Set.of( + "ConvertFogStyle", "ConvertEquipmentType", "ConvertItemTag", "ConvertLoadoutSlot")) { + assertTrue(UselessFunctionCallsRemover.isFunctionPure(name), + name + " must be recognized as a pure conversion native"); + } + + for (String name : java.util.Set.of( + "ChooseRandomItemExWithFilter", "BlzPreloadModelCinematicGame", "BlzCreateDestructablePitchRoll")) { + assertFalse(UselessFunctionCallsRemover.isFunctionWithoutSideEffect(name), + name + " changes state or consumes randomness and must remain effectful"); + } + } + @Test public void trvePreservesGlobalDespiteLexicalShadow() throws IOException { test().optimize().lines( @@ -2903,7 +2955,22 @@ public void clientLocalNativeValuesAreLocalitySources() { "BlzGetLocalClientHeight", "BlzIsLocalClientActive", "BlzGetMouseFocusUnit", - "BlzGetLocale" + "BlzGetLocale", + "BlzGetModelCinematicGameShotCount", + "BlzGetModelCinematicGameCurrentShot", + "BlzGetModelCinematicGameRemainingTime", + "BlzGetMinShadowCastingPointLightCount", + "GetCameraFieldControlledByInput", + "BlzCameraGetCameraType", + "BlzIsMetaKeyPressed", + "BlzIsKeyPressed", + "BlzIsMouseButtonPressed", + "BlzGetMouseScreenPosX", + "BlzGetMouseScreenPosY", + "BlzPixelToFrameX", + "BlzPixelToFrameY", + "BlzFrameToPixelX", + "BlzFrameToPixelY" )); java.util.Set intentionallyExcludedSources = new java.util.LinkedHashSet<>(java.util.Arrays.asList( "BlzGetTriggerPlayerMouseX",