Check version
Check for duplicates
Check for support
What platform are you running Path of Building on?
Windows
What is the behaviour in-game?
N/A — the node-power heatmap is a PoB-only feature with no in-game counterpart. The correctness reference here is PoB's own cache-free computation of the same per-node value: computing calcFunc({addNodes={node}}) directly for each node (exactly what PowerBuilder caches) gives each node its true delta; the cached path shows one shared value for all same-modKey nodes. Driver output demonstrating the difference is below.
What is the behaviour in Path of Building?
Every member of a same-modKey node group displays the power delta of whichever member seeded the cache[node.modKey] entry first. On the attached test build, a 5-node group has two members whose true delta is +0.577%-class offence and three whose true delta is zero — the heatmap shows the non-zero value for all five (over-ranking three no-op nodes). Which member seeds the cache depends on pairs() order, so the displayed values also change between app launches (8 fresh processes produced 3 distinct node-power tables).
How to reproduce the issue
- Save the attached driver into a stock checkout's
src/.
luajit powerbuilder_conflation_repro.lua yourbuild.xml — scan mode finds a conflated group on any reasonably-sized build (no special build needed); the attached build code reproduces the exact output shown.
- Compare the DIRECT column (per-node truth) with the POWERBUILDER column (shared cached value); rerun to see the shared value flip across processes.
PoB for PoE1 build code
eNrtfVtz20ay8PPyV6BUle_Fso25Ysafvad0s6zEshVJtpO8uAbAQEQMAgwASla29r-fngFIAhRAgpJyObverXJEoLtnpu89N7z8n6-TxLnWeRFn6asd9MzdcXQaZGGcXr3a-XD5-qnY-Z9_jl6eqXL8PtqfxYl588_RP17av51EX-sE8FzAK1V-pcuPc1rkMzybqrQc6yw9Vb9m-XEWvtq5yBKVx8WO46s0jMtXO3tJnKsdJ0hUUbxTE_1q51ylVzrfcVQR6DQ8WL4wvYjiNDQvg7HKVVDq_K3pwt6szE6zEGAilRR6x5moOL3Igi-6PM6z2fTVDt1xrmN9UwFdnh8dNToXp3Xnzm-LBNpQOzDCf7w8S9Stzi9KVToF_ANdBUapK32oJvAv0FPJDIgR-oxwKRDeeb4WbX-WF-W2uBdTrcMl-DPJXdILfJbroyjSQRlf64M8Lg_GKg2WjbE-vA5Y_MxbB306S8p4msRGEjUGetZL_83drtA-2MusVMnh2cWSLuHPEGKUEcGk2ICXlQs8tw_yU1yOAfAebRjM_QQEck_ck6s0LvU9kA_UrCjjwKhy2mqcCk5c95lAElPisXWNn2VxkaUP4Ow92j2YJQn4iyZmr1jOdaHza1XG7U72wh9kEz9O9So7KAxKEsqlx1GvQeZavY8qUzlXYTwrTnWZ66Kh_b3cOFWpOsiKpZ6htaBnOgcnVrYw3A0IFzrIwO81URh7RuF_YkBT3QR623wbR7oFideCbjWeGqGjO558hl1OB7S05XCOLobCbU34fh06B3c_DPIimyUDIcuG2-VrLOq3FiTrtdND_XUpf7SOYBMSeb0x4iRtKhRaQ7AJiRjq7-F1ZlzDZuZYd3X05mwZNIVH-TPiYkww8WRv8BzfFnGgklP1NZ7MJhCzLtUXvWyQC7lGX6_GZQqerg8ZHFP_2F7Hue5DZMhj_ZE4S8J7tQjROCv6MCWjqN_PxOkbSNz2gmAG6dft0qD7UxhjzMtuMVesyXWCFwb6JA2WhPk6uh_S3IaNRo60rgWDcg7GaxIyP9GDcZbN1E5gqbKuu76xK53WLd42sj6BnqF1eG-1DsbHwOxzVS676T5zuSQMoi5xqeTrgsECRxBvLb8NcJPfa0NMB7sR5cMw7nDOg5BGMJWei8j6INrNRSCwtuluHtJn2PMQZb2oR6nOr24vxrFOlqPszyWa4PMuHqjpAEzL_iZ6UwyIDWuwizHuENQeDevNIFUeDgt22_bpWhVNv065129SFcsqjBa31iihhoQdUEK9UoL0J7xnefarKaKSLfH28kk2yxsBY_04KvDmMDDdFJyq8vFch7OgFQ1RvzrPC8L9BMrhlbG4G7Ggp0myFepeWargy2EWXumhKLaRrTDa_buYTafgZoxWrBCQ7rqgCyVH3Mi3hgC_B61umjddG5wHN7AEXm2AbU46BreygrHaFPfW5gsrzZAh0KtNbBboKfiNCcQJO_FxmjXnPqjbH6VfQ6U5qGy0gO1ithf2LLuB7o_NvFaxHTTkVsuO93Yl1-nvt6v0h4EPauAoDSFLA3tYbQMNxehq5jKegE8tikNVKies8_KPKo9VWmJD2ym0yoPxW5D_a5UkPriDVzvNp-bXCiKad-rlczuzaP46mUyzvHT0V_OfM5WXt_PpPQtonwCdooxTO21g5hKTHedinN3shddmFJdZlhSLOUE1neo0bNG4zLV21NyTBKYTdozmhxMks6LU-RtVjF9n-UQt5zaxmWA0724rvYU2_sVcyNR3OeX437v_IsxD8LfwGPzgjGGxiwX3OPxijEp4h1zpwS8spSS7yBMM_7s143kSGo47aQYDNGmmwBIIMkF2MZKC7mKPM75LqRRiFyHIonY5MzCYUg_vEuxCI1J43i7GHIldIgVHuwT-I3cRRpTsUiw5kMEehb7ZXhDMqbuLXeF6u0h4XO4yhFy0W_fYpfDWDowRaHEXUQQ9IMRjxLQNBDgQg-euMGQIIXSXEyTpLpPEgyeSMnhCEQJiXBAMz5lgu5i5TAAkNowQSEDvGXMtTQzUCGS8u8TjvGqFmhFKvisZPMZCMGkeI8-QpDBCSoFFgE6EGQ8HYhwLz5BnCMhTwncZ1EbQIOLQcSDlMoMLJCljlgseN8-l6-4iiWBA3APuQokADZo-cfMvjA06A2QYpOLQCDVIXCIX-OpCP0yfgFEuNf1DBhwoesLwFVhBhStNvzEMkHBhhs8ECBJakkYACNux2scSmO4iQxj-3SWIm_ERSGB3K70CKvAS2A994VgyQ1DAUEG-2AzYxbugk55RTczZLoJxe8B57nKQEfwwI0DAeSxA1MBrBkqFgP2MWR2Gihn4DA8AhCDoe63n3HaPS9eyw7X9AB5ywgmIGht1gGZB27jrAeOgjhUcOsUr2UMvhREOsJMYCVE7YMAEmcA4QEMx9E6YYZmBC2T1FfSSgKIK40vMDIzKb_faFoPAZEqw6cYqBBb14oJ5j615_-Plh_O39o9_jMtyWrx4_vzm5ubZVJXjLNJfIfF7FmST51NAAsfwtPgSJ8lTQ_b5Hvxv_-qE-LfTd5-Pwo_0-OYk924K_vHpz9HHYpJ8HKuJ9-no64W8nsrzn8mbD9OI7p-fu8n5V0Z-vnl68_NhdHamfw1Pr2SKjg-mP3-69TGeRb8FN1528-lNLH9Hb_az367fTZ5evkFPb76_lDczqn7_-sP3n5MMf5Zf353sv_vlg3j30_5X_-SX6PP1gXeUnxxn-Jo__eXn33__4c3t1_zp9DD9Ghen_OLjDx797J38-jlTv6LXp-Ev9Ow3cn3ovjv8_PSM85NZ7jO2d_5Onr-h-eXrpzdfPn__9NdPPx0dnr2-PNn__mRWnMsvl08v3138cPnu9uuXnz4fvU7K8t2b8kxMf9j3P_20P1aff_3l5Je4uDrde09jOpXllM2CvfHp_vc_vk72z_f2T_ZeT_bOv7w-_vHjj3sXJ_s_XB1d7n3a21dXr-P9jyevP10d_bh_DvBXH19fA9-839Kfbz6j_DB6d7N_9pkeX3rH5z--emUF-HwuwZfVKk9RibP-Zd2mkbc1vR0nLvXEir_OWToAjWttALI-QOt9l4DE7QO0ltqgSHoBjQdtAIo-QOsuG4Col6JxYQ1A3gdoLboBSHspGrfaAOwftYkwDUDcy0cTeJaAqHfU1mc3KMpeEYL3bRCcw7183tSQlyYjzWMIqjWR-W8nBG9xqcoyy5wscsqxdi5VoZI4cy4N-DiOSrPaFQc20cjL5_jQ_HthnMMJPCzm_qJ4XpSzNNRfn03Tq53lOMD37dS5RpU1nJharknrw4l9VDw_SY_VBLwNpDBFmavkEnKk5PlZRd_kLFVHi-d1F-G9r-vX-8e23Wp4_0Dud2apFAg5ZebsXWdx6PgaSgDnAjqZ6rDiwvM5G_5srtio-I0tq2yx0fEbW1bZYrORb2xZZYuwOdE3rqzYkCl5vrFllS029f_PYMvNOFPOG5370LSZmRrCkwhKgS93gzOm6I_wt6aH_Tyh3zlxGuRaFTp0XpuOOVXbzuEst1Maf7muEJMf_p_QleVPm9pB2jfVgZ1QMm2bP95lZfXOPJz_eGmZUThFlpfHelLs3x6eXbw2KyQrW0zqWSIDfaHLap6rifNqp8xn2kxsRWqWmOc_zmCYZtLKbT59W22ZS82M0nyxGUiNsxszjV1RvLydmlmfvbdvqzdv9ZUKbk1T89mrapaq7owTh_OZq_qhUySZ2Xilk4kuq91we8v-286D5iUzEHNaT51Cl-LkDuiBSoLCwuvULJmG9TBrTYQu1S-OkwzMEM-5UG8MxO5O6z2av7-upv2Mkp3pMo-jWIf7SWbme2253fXitzk_DdWrKs0_1aUKVamen0DeXzw3LKrUG_5axW-PwAmyWVrJMQW9rab_FihOhfN84Djp5mHW0j1KEzMLb9bbFwO9-2o5VLR-qAuV2QvD2HgMlfx0Nmioy9aGDhKjzaM8zpVZflkMrf79W8MUBkiuxhowigryMUdwqgA4S3TaGkfr6ZajqRbtjHcyU-d7M7PddcDQmk0uSllD8q6dnxvfiP5CO4dqeyNjIcBN9KEqxg22Np5tydQG5gBeWmjHgm9g5X6WlcXfnJO11Z9muZ4nCne9SfvtYN-5cCgn88xkSWSI0kKri-xluF0OCBNmi_dkdazNh9uGhybugJE1wNcMq15q6h9XDdAY2EelkjdmNakxrsazLe2igTlgUAbaqcAfU1YnkwkokTK2kDRG1X68lBcZNLI29mJwNUO7RjfHcCzKRsMPb535Fo475o-3NH98T_PfIpGYTM2Cc0caMX8xVHM6cgibpA5LI-rWHlN96h59hLz_VKXxdJb0-Li7ENv7ubs0htiNKUlaWM8f0b8bq6y3_O_leXaz4hjar7Z1e3cpDPUTNZZToT1mzlVLwxJ-l12ru5JuvNpexA3kIY7eQDsW_I9Q6jiIs1mx3GNWdKh1B8z2o57vGVuSqbdm1UeQhoi96ojT7MkfwJOLmzgq96IoiYNuO78DsD03ztU0Dg91oG4Hjdy26DSa3BA9Pmk1zdK_NAffwtCOogg0w2zfvhM8Fm-2Z_ESd0jcmAM_ph-5zL7GwTlwtTGwxrNtM44G6oARWWjHgn8Lht-ynW1t8r8nMAznyWlsDs7u5cG4S_Ttt9vzoY0_pLi1CE6NsSEiwOCudfE3n3tdeLi9pPy5y2lWz7esQe_rN80ihjmqqvM4vXpUfar2zjYnnuoH28461WhDppwq0D8gFNSnai_KPP6i75rFyuvt7WKFwICx1hhOjfIHjNkc6Imy_EqHNj3v8I13IbaOCHdJDJqmnSM5NdZ9J2utD-hxDYnyDcf6fISFWelskc3yQL_aMUN9QV-cqTTUkyyNZxPncpanu86HtNClY7qzxqVUrde1a4-4FqI41MVUxSvBuJuHlu6Si3PMXt7dh1vvU-cHg3uapWaztnP0dZpk1Wmj7dlokUN9b1bZtwtOHaV6cjsnWT02-9kt4tbMW2dwf3WnGqKs_7zQpV2WrRZezZ-W-xbiJJ3OSkvk1c4kLoLP_iyKTqvulLm9lOTo9eujg8uTj0f1cYQmiu3y53Q28U1Er_5rsswK8kLbUs4pZn5R_WkSGX1jO3IIniFOCjOaJFHTojG1aFZX654ngLeGmoV6Ey9u-eimtQTop3T0VecljPeTyoM81r39Wrzf0Kmqwaxeg-qjZu7P6CdUZYkHqiirM4M9nLI3lvRTMdeH9A7HvFyDezKZqqS35frtBk6UZm0dNDSG-tOcfFkvcrMSX0Gt4cviMHSvvOtjaf007LUifQSql_3I1dUefdj12zVctdeS9HK1etuPXk-q9GiUfdmPvDiGmqXApj4qC6g1lN5lqVVyMJq9ODFHyHolewQVzxykn-B7k4jW53v6KJ2Cj5qDrDWcPPZnZb8ZNyDW8MoepO_hkHnXj1odCe8Zg3m3xhO1zkP3MLQJ00-qOkTc68jWoVbHCXv5V59NXCOCemGnh_3V2zVMmJ9S7hl__XqNkVj_a7cyVQdUe8xlBWydw8iCLw8nY0_dPpzM6jHch1O02-F65V2_7Uf_UMYmXemgUuUsg4gYo3oYBWNbD6NwCSluOcv1vQmcr2YiS9zz9TnI4tRoJ_L87TrPUR8mvTeFebl9T_SzambwntjW_x_qyGzAWRsAFjBrBgIFpkk46knVB9EyeyCXezIeRMoOsdspLTm1Fa0qrHZybWuKlbOoL6dZ508qkA2EIC94sybzHEZpcUz9jVaJuVMtSx5G8M4lPA8hZm7dmE2hzp-Te9-V7y_lMJB7WVkATXss-9Bc7vFQHpoqs4NQf79ePp9XiPbAtqnZ6tPkF2VuJkV-z7LJz7Y0NX_VO2xJvasWsvfDGOSWW32bt2MAf5qf5n9pp4PqLb7m78UO334Cs0JX94BVS38Wo7E311Bp7ss9V1Da3L5wzvfOj0YHKs8NW37Qejp6a_4600k5qtOIFw6TbP5jXxXzq9DiRL9w3GdCemz0IY1_m2nn5PCFIwSKfKZQpMLIxUGIheuGCAscKh5y5KNIY8mJqwlWyKPCpR7lirHIwzLAAR_ZrlqmATE2qrctv3CwO6qPzkG3n-4_PX66P7Jg5_q3F46HR1BwJXEQm_d4hN3mbnYT-Bx7FYyuPJVjasYRxt85ubm9ZA5iLjwz075VbVmM_hWZWz0h3oX_foJocxv4PLtwbK7hVDoyQqzZas0z0xaY--gJItKgTiojc0w8Hz2h3nfmobnTw6nyRtMEPOf2ubmKo_n8X0GuotL2x7y2DDA9rlhjZv2yI8dMJC4hUYsV5rZBg1D5DjsncpqF9pJTJzf_moud2E6tLPWsSS8E3ghBNkLQjRBsIwTfCOFthBAbIea3tdn52rZd4bZdfXh38uOHo9GlOQNhzgAYjhvXN_oIJGdQXDj7xsQadhMxggPPZW6kCQop8zyOUYSoEEIhgpFLGZeIKe2qgHmR5FFIhZRgbB43GKOLsZrq3DE9atuQ1zQT0TQTNHpCELWnGmzJ4YBjXdVa5LbUxyitM79PyYnybFIdFSlWAWuT6wCs_mPuv0jM1nZo_TabOWN1rR3ctp9KRW2TNdKVWZfBTp0uOdoSJ_UB_GJkG22Cohr0DmSdkbeASS_d_zYr6dJw0qnhpyrJUn37_yDoFP-_cE61cZFxMRm9z1Nwsc6PM4hheVPRA9fV0tOBcgX1kIQowBTWXkBCj0URCXlAfU8yqTiKWOS6foRIpHytXE-wKJQt5fbQMkA04sM-xIdmdKCsrfZvVAGqUYHbH7j-UYwu8_jqCuxILV3qfnbjVNP-N2OdWm2tAoRzE5djgASA3fnfqFYa8NzgurObdCRb7rdCtNOSoydM3AkIrBll9pPYWCSkKLHxIakDqVTV0DxC_WdpZpfe0Y6MZXX1aLRcPGqqmlYo8MBRytDFPlI-kj6LKOaS-jgQkYyEDpHU0o2iEPse4aCUkIVoKRW8BgG1_Chp6FdDt7hYo1uNBOJglhd6IUsrxHqZqRYsOGNuhH6SlpBSxFfa5gEm7YCHVYLhGG9qA4mzvPMacgXWVKNq-sup5r-AQOXhW0q2yA3eZelTMxuc6nrJtDp-Zp3xU8_g2QtMl9nRN1doyd9VyfNZqp3jTIWjjzoxsdQe3Vgm0RSvSaKZh2lTcb2Iu1JicJQhlgIkGGFPEOqCt2Q-QowhRSQmOoKUAHEsKQoxJA1SgsbD_8PeJPpODn3e0GRBV3Jocvfg41Fi70MDlZjPWY9a50b3qgqr8m_bps9yXfYsvcHJM0GDOs6bzZl5DJv_2KnMRebSSKNFK72pL4abj3QB1WraphXv0yJRs6tx6URZ7tB5UmGs3qwHf0vB75GCr-OY22-4vDOH-XGmdTo_svw6A70sR4f2IrdbsCFlZpKWdow5ctcYMvJ405BDL0AUEh3scsY9HFHEuYxo6IUqirjQvsJYuiSgSka-K6kHZg_5PoIUyBdM8NUI1FMNH8__37BmJtvW3Fcw2mKRLwtha0x1uCnNpAeUyWKdYXruXcPsKWqJ6CxqnyBpny9uhGy-NDXBokhvWx3qt0jH1ELcdVd6u-vMpqYlj303ekqxM9982BpwVa2Y6TnHv3X20niikmJ0kOX5bFpWx72_GeyfY7BeR6Q9zNUVCPRdlhV6tHdlqoy9ySzRrXKaUMjouKegVNYMAqT0w0gQpVlAEfapUhIpqK5JyGhEAoa5AijCfeFrwFR3pqEa-R5r29XC9e8lEDmgN4XzTkHQA6U6B4UMIdsbPcE20b8oc51emTIB4kwrzWtGSsw3Znxm4qrfIE3cNlZmLj5tm1nLAygIvFVZY11Ax9uKQgNmZfbpNE7rjLNeWpkX0N9M5I6JdGm36AxHn2IoG8cqTkYH-SzUprpsarb0CYZaJvRU4AaUoNBXUaC0iJCLmO-FyPNUJHwX6mgkQe0ZR8SNAt_3WYiZ8Ibkhvs2mti40lR2d4RFy-f6WTordDV9Aj-t3zz6bRZPp_CrLv2385tdbJIdTuBoMr2FTqTOca6K6Whvoktw5XdLQCIkIySIAu16BGlP8ohjIkPwDdoTCAo_FqjQF24QEoW00pEbQKAmOCRIMEXW-AG6MpXWb3ZscwmHsE1vV02ZomZsNVXYqD2dWzHFGGy1aBCZb_KMnlRx1tjwMu9ddqn4VgA-rACEkNURl2bFF0h5gEWfbkHGaQF5BZSEapaWiZlZquZYIWQQWv99N380N542ck2A7E01LWhD06kQ2kM4MlfYkUi4WhEUMI_70nVBxwXnKvBAvSNKiMs9NwwB0MWh0NSnjEdD_MLdLHO1ZnzCNpd5T4wKLyxlnVk0wyJjzRA6apdj7Wk1hPmdlLS9NNNjNoR2m_CyvMNr1lKWUPwOVD30b3HxcZZbUNc65qGZErTnYUYHcalCSGRWYqeL_YAg8PBICcwCHHiMY0QVplqa8ssstXhYgcH4RAdIcZ_5kfBCA8HdoB07ubvORlarMdEOpE8QsTFBfy21GcJoLwwLxz7jzRKs8ZzhRoFUv4Xol0KZAsTsKokpeuyCtp1P_PsoW6cIcYcIX5ts_jwLR_t5psKxVvUxho51BCiYwwgCOjgyLITn-zoMBTg1CPSCIvtRFT9CPvN8DbE9iqTwIy8yiwiaIAp50BtwzD0LZg259iX-aN28Pt4Y8Zuz-HXQBTQ6zC92IiN8d563cSDOOYtN-IDSw1GLo4bOpf1K5oi0nGPdsm1ksfZR9DrB5si_JQf21u-WYp_uHZ8cjPaSYKwnEFDmS2Wg0cGXIk6MfKvb9uoZqIOx1qUat2K7R7FwQy-izOeQnzIauGHAIKyjUCpGCai20h5k5yJ0IcENMUGRj7WLsSJEsZZ-I9dtzSP1L2UsJP7BCNoufs1XSEH4wdiJZkkywt5ygmZxU2B7JVdXARLRNRM2UEIaxzZkX8IjKkGnAGmXAA-V7yc6n4tvPy4mMzCQlujMsUG_HXK0KzmWAoco4sxjWigoRYiWimjsQW4G_klFobnJH1HEUQQg0qNB5NMwZEiidfth-ouRgZITQyVnttM03EZnUfF3kiDrkuCFnZYxc55H4PjNOJZbC-yunyTR13HFhaYEwY5CV2Np1l58F0HxiMznIl3fbF_ypQexRzKoJaG6JmGEEESdQPpcYu4TEbm9c7ikP7q4I97ObyfmVNN8VQIcLSFL0S12WZjBzVOCk8lklpoc1y4jg4EZ0ZgFCLlYgIgjs23MqAcEiAVQF4FFDV9dRLiWjrLiN5MDt6t4f_eMhA9y3JfZVP3etnt71kc1lYYrn0A-GSAZMJdFmCLQFzcSDDPGzMcbo1D7rqaSRQFUbQz74AAUcwmEccyobisN7zN7715mT4eaPXf75xm3Mfc_eIsA8gYJ7qIj2P6gvsDb1torwpiGCAzZ1SiS3KOShCrwIZRijqMA8kcaKgUVNMOuDAJMQiigI18oyjkU1G3J4cd12GSo5FjDtde3-MKI7R4Eu_JoNrH8jfx190zoqbYfeF8szUEaC6WdOSxaOt_rG5005QapkBuFklEaeNw1nxnCxBNaC4YYlxKbpEgJTVykmcRCeiFTNHIpwp7HI33H4lre-KmHNsyoPaULgMVGjE7A7tipoOILlJ03Bb95azYQjKHY00XpnK4hB4IvSqiS_jTP2im9rgnaH3K7lGa2gZlegyxDs1vnoPqK1V3xEeFzgZiSlEvfLIuCrYF_VCELkZBBKAQiEptqjkgpJAPTM7skuQ60j30t2tvCSLP0bpkaaSzYmNKaOvWlz3fLHAcAQB6t904Msqp6v9jj06BnNhJMjA60iTpXJqaC1W--YnsNEZUU2ZzSE7JBH4fSoZ0rtb1j389h6FUCcJaVOg1u-2EvpvEXbTbCpUEWzO8P_68vD7Hb6es-pGm9aniSmq0H4JE_xnkcxlCw3zEWDJWf9nQIih9GBEKTCJEkvqTg_BSYSkADziMlvNAjmHBfUeyGkG5ECErCwGOrRUX12fkXlbq0Pd98VbNHl2KTDhjkSqGcNCvHjSTSYMwXicCLf4Ckexi5R1KVTgF0zR2-95P42u5cSupVq-4oI6kP_sYjkBBo7SkhCFTaHHgaEGImo0TgSawlVUGolfniGAuF6zJOvcgPINq0OY_avG6tAn_Xns3p2BnxxJu7gOWB3ZV57jsThog0Xzdnmv7myTnu32FvB2nU61QrM9bRWQ7FuTKXQ96Rny8JdSmk5WAxPAx9HnmeyyGySPOpX-T5kOtRTxjRcWG2iGqwIg0ZhMd9FHK3z3LemlytLc1eS1n633ZA2FoanXwiXaul6goqsw2qTSPQW5drTSNElYt91xUk8IkrhatCKZBgXoACKSUEZKhtuRtxKF9AswOfK5es1rnt-W7Rpaut4Z_G6WLWu_WivbVogGH8HbfqdMqqa2_zR_BD4VxWm7KmwHxdEeqRyIWSBFEWaAHaS5XHIqmw0KFGmgvKpAS_7yMkIwaOi6kwdCUhKxtFPXdw1sT-zKypveK9nNNuLJH8mSnT4hyZdtxnUBaA41ksvlRn8PpTonM91YCYr0maxnmsvxiF3s-S8tvRk7j6_mGX719828X5GJuJiTUJk1kiEp7AVEWCax6I0A-ZjjiLPAoVhGLg7LRQkCcRz8UK4jsSmoeSGC8YEbXq9tu-jbWX8mCI5qsdeVEv5qxbDK80y5yFum1D9-1adlTpMPe7uiaOc-ej-R7yH5ku8a4DGIm6AtZ-hOEAo23w63dSkjJfhcB5V_rMFyERykOBMgdAhUYKinZgOzdBl0lBFOGSSwJezXiswBVqNbA0ZlHcdU5K9Dop3GV9VRHedFLFVl4K98byezkWtp1n-ZAW4Fq-mKuwnOM4gr6vAb1R1_ZWQ1C6o-s46Qf9FAdf7PP_wn3qncbQtSEV6lz45Xyf3fbmVoFZEOChEsyc6xA0FFGouNmA5oc-iTCkW5BmEdcPGEJMQMTWoQ9hPBTgjMA7bZ9btcoAc0tWvcxWTdKPs2pmTc2PCf2XJFyiS3zZLHEOcrN8vSnd8j1wWCFyEfW0iiCzgtLOrAe5RDGOIygKaQgFIsacBfB_yRUkyiEKfGb2Xvmo35P9jSapHivdop17_R8rWfuWYj2uY1szh3sEQb6y8r7CkQsNDi1yBQ4x9TkOtSKe7wsmPagQI1cyxVyqGHYFWAILAACHAcaeihgJ1jo32RtV2YNcFpYbF0j-z7g10rUd9W2myrFzUWap3pSgBYKFUD8Gds5KE4G5yYwpJxgilsc4VJcylIqqMJBCMOxxhjSLAhFgn60K0JP_sQla7ZRWXdq3nOyvcl3ma5P9N9-0P0RpFeYk_HD-dvWD89VtrZVeNb5Bf-YD7PLm2IskK1vPa7zltzbmH3sXm1DsncuND9i7mxDMZSHOnn9bFGqu-g7eAn_ex1UabHsa-BFoIOfiRk1XCfFHGBB_hAF10ujQHkKkkG3tqR91aU-X-glK-Ir6VY-2UD97zHx1CNvoVvVV1lUK6IHaeS9BdGkFewStQI-gFegR-kG3ZOoSHD_Ish7DVQyjUd1ZNr-UrKJYrXM35IjIvZQaP1AlyQNH0JAe4ps6UH1v4yFK1G2X_IGDaPQAsXXOiSNBaNs51Y-2cE7dXNjGpKuTvUsE7776Sx7BB5CBkQEjKVZ4Vz96MO_oA-XfMCK0kZfHJrfeSmltZtNoQ27l-5d4ZBh3GoNx7-VT2FCJYo-KFYlWjwZJtF8eDZVC9H4uAT_YqTw8XdgqsnUJgg4UBHc9wlbcUvXoHil7Z7DcKi9ffqX18V39oyXWaGhCS6kgKwlt9WirfLSXJXiojClb7Uf96KF5Md9WTZfw7H7-epsWuykMDTpcopVatnqyBc_m3wKat0fvGyjpY6kuexxjfLgUHpp8DvVvBHt81QarR9vofkcH0ONwkjxWXbe9f2xEyge1jB8aq-5TTjY6j7dg_xKND0vW8OOImT4OmXuHsAa70Po5GI-tmkv1aOAcDEH8TsyrHj3Q3thjGcpjFC74sTrzaNM6jxYhhjpWJplYSRzrR3cEXU0yVx8GM3e8q1Bf2PvdP2mzklRUl8DbiyPs98KyNIqv6hy_-lFf2G7xF0-cMi4TbT5CFKmZndxpTE83vxvmqzQ0Xx-af2NsL4nz-de_W4D7OUAW9gD2WIeXmT36v_zSGOrCmRW6-o7F4nsafpYlWqV1ddKBYibX7clPS_9gHCeJ-TrURrzJ4sD6O2Cff3uucv0-r9ad6ntQN3S23fL8hOI2Xd7X2nzK6Vybm1OS2wGoU5WWY52lp-rXLD_OwqUcLrJE5XHRhXQYF2aV5-jNmfku1Pu0_h7O8H6aY1_2YKEtC7forvkk3eKkW3Gs03i2lUzNN862aG5VpsPkuOBpnK7w9Py2SFQ5VpulX335q0_vzhIVaLOrRec1tjZI1effFp3zXHcDgl0WrL_ftEAjm7AWK8zzT-3NMTGmaFOL-eKDbAs01o9zMQbNurCXP3cwva8NeHKPrplTQfdAqz9gPccQtB9-qU7Vl3Z1aId2YE47X-gkagyRDRjjquDYYMGdma-dLOQ9FGvr9hZf7loMy0WUDOGPtYE35vjMDTQMXmKiB8l_osFrHtZL1duwM19-OHKwHRgZbMVJa26rzQjJ-9Gmi_1IdwdF3QEKvY3Mluxvc76yv3M9gVIx3L_dS5LbQcK4CDL7mZjtrLfiz3mWJHZtvOnONmDOi4g5AkdmfbIXp4iv4uR9ZD9UBZ280sM6GVQZzhTEkqvkMr7SuTXgQf5sIZet9GZ-b2u_d3r5fJF4Vd_ssb_-OXr5_AzizfvIfkAI5PnP0f8CEQtACA==
Screenshots
Summary
CalcsTab:PowerBuilder() caches per-node power results by node.modKey.
Nodes with identical modKeys share one cache entry, so every member of a
same-modKey group displays the delta of whichever member pairs()
visited first — a per-launch coin, same pairs()-order family as
#10027 (separate issues per your preference in #9947).
For most twins this is harmless: nodes with identical mods usually have
identical toggle-deltas, so the shared value is right for both. It goes
wrong exactly when same-modKey nodes have different true deltas —
position/structure-dependent cases, e.g. a cluster-jewel notable vs a
tree node with the same stats (different dependency cascades on
dealloc), or nodes under distance-scaling jewels (Split Personality
class). Then the heatmap silently shows a wrong delta for one twin, and
WHICH twin is wrong changes between launches.
Versions
Found on v2.65.0; still present in v2.66.2 (current release): the
modKey-keyed cache and its shared lookups are unchanged
(Classes/CalcsTab.lua:606-618 — cache[node.modKey] /
cache[node.modKey.."_remove"]).
Evidence (reproduced on an unmodified v2.66.2 source archive)
The driver below is self-contained: run it on ANY build with no extra
arguments and it scans same-modKey groups of unallocated nodes, computes
each member's TRUE delta directly (cache-free, mirroring PowerBuilder's
default-mode computation exactly — CalcsTab.lua:477/530/547), and
stops at the first group whose members disagree:
luajit powerbuilder_conflation_repro.lua yourbuild.xml
Verbatim from a cluster-heavy CI/ES test build (2,589 power-carrying
nodes) on the v2.66.2 archive:
node same-key? DIRECT offence POWERBUILDER offence modKey (first 40)
4219 true 0.0057731675377536949 0.0057731675377536949 [10 = Evasion|INC|-|-|-][5 = EnergyShiel
46136 true 0 0.0057731675377536949 [10 = Evasion|INC|-|-|-][5 = EnergyShiel
58649 true 0 0.0057731675377536949 [10 = Evasion|INC|-|-|-][5 = EnergyShiel
62795 true 0.0057731675377536949 0.0057731675377536949 [10 = Evasion|INC|-|-|-][5 = EnergyShiel
64501 true 0 0.0057731675377536949 [10 = Evasion|INC|-|-|-][5 = EnergyShiel
Five nodes share one modKey; two genuinely carry +0.577%-class offence
power, three genuinely carry zero — the heatmap shows the non-zero
value for all five, i.e. it actively over-ranks three no-op nodes.
Cross-launch nondeterminism, same build: over 8 fresh processes the
full node-power table took three distinct values — at least three
conflated groups, each flipping between its members' true deltas
(0.00577… ↔ 0) depending on which member seeds the cache in that
process. First caught as a 2-node byte-flicker in k=3 fresh-process
comparison testing on v2.65.0.
Fix directions (mindful of your #9947 hash-weight constraint)
Per-node cache keys are the obvious fix and exactly the shape you
reported abandoning in #9947 for hash-weight reasons — the cache is why
PowerBuilder is tolerable at all, so a heavier key on the hot path is
unattractive. Two scoped alternatives with zero cost on the common path:
- Hazard-scoped keying: key by
(modKey, nodeId) ONLY for nodes
whose delta can be position-dependent (node inside any radius
jewel's influence, or cluster-jewel nodes when the group spans
cluster+tree). Everything else keeps the light modKey key.
- Hazard-scoped bypass: skip the cache (compute directly) for that
same node set; the set is small in practice.
Either restores per-node truth exactly where conflation can be wrong,
and leaves the fast path untouched.
powerbuilder_conflation_repro.lua (self-contained driver — save into src/ and run)
-- powerbuilder_conflation_repro.lua
-- Repro for: PowerBuilder's calcFunc cache is keyed by node.modKey, so
-- same-modKey nodes share one cached power value even when their TRUE
-- per-node deltas differ; which member seeds the cache depends on pairs()
-- order (per-process). PoB v2.65.0 / v2.66.2.
-- Run from a stock PathOfBuilding checkout's src/ directory:
-- luajit powerbuilder_conflation_repro.lua build.xml nodeId [nodeId...]
-- For each node id: DIRECT = calcFunc({addNodes={node}}) computed for THAT
-- node alone (no cache); POWERBUILDER = node.power.offence after a normal
-- PowerBuilder pass. Same-modKey nodes print ONE shared POWERBUILDER value
-- while their DIRECT values differ -- and rerunning flips which member's
-- DIRECT value the shared one equals.
-- Usage A (scan -- works on any build with no extra info):
-- luajit powerbuilder_conflation_repro.lua build.xml
-- scans same-modKey groups of unallocated nodes and stops at the first
-- whose members have DIFFERENT direct deltas (a conflated group).
-- Usage B (explicit): pass the node ids of a known group.
local pathBuild = assert(arg[1], "arg1: build.xml")
local nodeIds = {}
for i = 2, #arg do
nodeIds[#nodeIds + 1] = assert(tonumber(arg[i]), "nodeId: " .. tostring(arg[i]))
end
for i = #arg, 1, -1 do arg[i] = nil end
package.path = package.path .. ";../runtime/lua/?.lua;../runtime/lua/?/init.lua"
package.cpath = package.cpath .. ";../runtime/?.dll"
dofile("HeadlessWrapper.lua")
local f = assert(io.open(pathBuild, "r"), "cannot open " .. tostring(pathBuild))
local xml = f:read("*a")
f:close()
loadBuildFromXML(xml, "pb-conflation-repro")
wipeGlobalCache()
build.calcsTab:BuildOutput()
-- DIRECT per-node deltas (the cache-free ground truth), mirroring
-- PowerBuilder's default-mode computation EXACTLY (CalcsTab.lua:477,530,547):
-- useFullDPS = false when powerStat is nil; offence =
-- CalculateCombinedOffDefStat(node-added output, calcBase)
local calcFunc, calcBase = build.calcsTab:GetMiscCalculator(build)
local useFullDPS = false
local function directOffence(node)
local out = calcFunc({ addNodes = { [node] = true } }, useFullDPS)
return build.calcsTab:CalculateCombinedOffDefStat(out, calcBase)
end
if #nodeIds == 0 then
-- scan mode: group unallocated power-eligible nodes by modKey, find the
-- first group whose members' DIRECT deltas differ
local groups = {}
local order = {}
for id, node in pairs(build.spec.nodes) do
if not node.alloc and node.modKey and node.modKey ~= "" and type(id) == "number"
and not node.isProxy and not node.ascendancyName then
if not groups[node.modKey] then
groups[node.modKey] = {}
order[#order + 1] = node.modKey
end
table.insert(groups[node.modKey], id)
end
end
table.sort(order)
for _, key in ipairs(order) do
local ids = groups[key]
if #ids >= 2 and #ids <= 6 then
table.sort(ids)
local vals = {}
local differ = false
for _, id in ipairs(ids) do
vals[id] = string.format("%.17g", directOffence(build.spec.nodes[id]))
if vals[id] ~= vals[ids[1]] then differ = true end
end
if differ then
nodeIds = ids
print("scan: conflated group found, modKey = " .. string.sub(key, 1, 60))
break
end
end
end
assert(#nodeIds > 0, "scan found no conflated group on this build; try another build")
end
local direct = {}
for _, id in ipairs(nodeIds) do
local node = assert(build.spec.nodes[id], "unknown node id " .. id)
assert(not node.alloc, "node " .. id .. " is allocated; this driver uses the addNodes path")
direct[id] = directOffence(node)
end
-- normal PowerBuilder pass (what the heatmap shows)
build.calcsTab.powerStat = nil
build.calcsTab:PowerBuilder()
print(string.format("%-8s %-10s %-24s %-24s %s", "node", "same-key?", "DIRECT offence", "POWERBUILDER offence", "modKey (first 40)"))
for _, id in ipairs(nodeIds) do
local node = build.spec.nodes[id]
print(string.format("%-8d %-10s %-24.17g %-24.17g %s",
id, tostring(node.modKey == build.spec.nodes[nodeIds[1]].modKey),
direct[id], (node.power and node.power.offence) or -1,
string.sub(node.modKey or "", 1, 40)))
end
print("rerun: the shared POWERBUILDER value flips to a different member's DIRECT value across processes")
Check version
Check for duplicates
Check for support
What platform are you running Path of Building on?
Windows
What is the behaviour in-game?
What is the behaviour in Path of Building?
How to reproduce the issue
PoB for PoE1 build code
Screenshots
Summary
CalcsTab:PowerBuilder()caches per-node power results bynode.modKey.Nodes with identical modKeys share one cache entry, so every member of a
same-modKey group displays the delta of whichever member
pairs()visited first — a per-launch coin, same
pairs()-order family as#10027 (separate issues per your preference in #9947).
For most twins this is harmless: nodes with identical mods usually have
identical toggle-deltas, so the shared value is right for both. It goes
wrong exactly when same-modKey nodes have different true deltas —
position/structure-dependent cases, e.g. a cluster-jewel notable vs a
tree node with the same stats (different dependency cascades on
dealloc), or nodes under distance-scaling jewels (Split Personality
class). Then the heatmap silently shows a wrong delta for one twin, and
WHICH twin is wrong changes between launches.
Versions
Found on v2.65.0; still present in v2.66.2 (current release): the
modKey-keyed cache and its shared lookups are unchanged
(
Classes/CalcsTab.lua:606-618—cache[node.modKey]/cache[node.modKey.."_remove"]).Evidence (reproduced on an unmodified v2.66.2 source archive)
The driver below is self-contained: run it on ANY build with no extra
arguments and it scans same-modKey groups of unallocated nodes, computes
each member's TRUE delta directly (cache-free, mirroring PowerBuilder's
default-mode computation exactly —
CalcsTab.lua:477/530/547), andstops at the first group whose members disagree:
Verbatim from a cluster-heavy CI/ES test build (2,589 power-carrying
nodes) on the v2.66.2 archive:
Five nodes share one modKey; two genuinely carry +0.577%-class offence
power, three genuinely carry zero — the heatmap shows the non-zero
value for all five, i.e. it actively over-ranks three no-op nodes.
Cross-launch nondeterminism, same build: over 8 fresh processes the
full node-power table took three distinct values — at least three
conflated groups, each flipping between its members' true deltas
(0.00577… ↔ 0) depending on which member seeds the cache in that
process. First caught as a 2-node byte-flicker in k=3 fresh-process
comparison testing on v2.65.0.
power-carrying nodes, and virtually all are benign (equal deltas) —
which is also why the bug is so hard to notice by eye.
on identical execution history; identical-history A/B runs pass on
both sides. Only cross-process comparison (or the direct-vs-cached
check the driver does in one run) exposes it.
Fix directions (mindful of your #9947 hash-weight constraint)
Per-node cache keys are the obvious fix and exactly the shape you
reported abandoning in #9947 for hash-weight reasons — the cache is why
PowerBuilder is tolerable at all, so a heavier key on the hot path is
unattractive. Two scoped alternatives with zero cost on the common path:
(modKey, nodeId)ONLY for nodeswhose delta can be position-dependent (node inside any radius
jewel's influence, or cluster-jewel nodes when the group spans
cluster+tree). Everything else keeps the light modKey key.
same node set; the set is small in practice.
Either restores per-node truth exactly where conflation can be wrong,
and leaves the fast path untouched.
powerbuilder_conflation_repro.lua (self-contained driver — save into src/ and run)