diff --git a/benchmarks/src/zoneStress/construction.json b/benchmarks/src/zoneStress/construction.json deleted file mode 100644 index 3445d934ef..0000000000 --- a/benchmarks/src/zoneStress/construction.json +++ /dev/null @@ -1,10592 +0,0 @@ -[ - { - "characterId": "0xb919122835f454f5", - "actorModelId": 9130, - "eulerAngle": -1.6433461904525757, - "freeplaceEntities": {}, - "health": 1000000, - "itemDefinitionId": 1378, - "occupiedExpansionSlots": { - "1": { - "position": [ - -577.7472534179688, 17.402490615844727, -1654.4932861328125, 1 - ], - "rotation": [0, 1.4982538223266602, 0, 0], - "characterId": "0xb12d863bac8922b8", - "actorModelId": 9492, - "health": 1000000, - "placementTime": 1770461450094, - "parentObjectCharacterId": "0xb919122835f454f5", - "itemDefinitionId": 2336, - "slot": "expansion01", - "eulerAngle": 1.4982538223266602, - "occupiedWallSlots": { - "1": { - "position": [ - -577.11767578125, 17.407991409301758, -1661.9613037109375, 1 - ], - "rotation": [-1.6433461904525757, 0, 0, 0], - "characterId": "0xaf7c0adaae2d2c88", - "actorModelId": 49, - "health": 995833.3333333334, - "placementTime": 1770462645592, - "parentObjectCharacterId": "0xb12d863bac8922b8", - "itemDefinitionId": 148, - "slot": "PerimeterWall01", - "ownerCharacterId": "0xe9392c463df83ba4", - "passwordHash": 1308505232, - "grantedAccess": ["0xe9392c463df83ba4"] - }, - "2": { - "position": [ - -572.21728515625, 17.407991409301758, -1661.6109619140625, 1 - ], - "rotation": [3.069053888320923, 0, 0, 0], - "characterId": "0x1a495983440aa386", - "actorModelId": 49, - "health": 995833.3333333334, - "placementTime": 1770462646285, - "parentObjectCharacterId": "0xb12d863bac8922b8", - "itemDefinitionId": 148, - "slot": "PerimeterWall02", - "ownerCharacterId": "0xe9392c463df83ba4", - "passwordHash": 1308505232, - "grantedAccess": ["0xe9392c463df83ba4"] - }, - "3": { - "position": [ - -572.5797119140625, 17.407991409301758, -1656.6240234375, 1 - ], - "rotation": [3.069053888320923, 0, 0, 0], - "characterId": "0x417af36656a6710f", - "actorModelId": 49, - "health": 995833.3333333334, - "placementTime": 1770462646989, - "parentObjectCharacterId": "0xb12d863bac8922b8", - "itemDefinitionId": 148, - "slot": "PerimeterWall03", - "ownerCharacterId": "0xe9392c463df83ba4", - "passwordHash": 1308505232, - "grantedAccess": ["0xe9392c463df83ba4"] - }, - "4": { - "position": [ - -572.9420776367188, 17.407991409301758, -1651.63720703125, 1 - ], - "rotation": [3.069053888320923, 0, 0, 0], - "characterId": "0x5b821bf4b8020a17", - "actorModelId": 49, - "health": 995833.3333333334, - "placementTime": 1770462647640, - "parentObjectCharacterId": "0xb12d863bac8922b8", - "itemDefinitionId": 148, - "slot": "PerimeterWall04", - "ownerCharacterId": "0xe9392c463df83ba4", - "passwordHash": 1308505232, - "grantedAccess": ["0xe9392c463df83ba4"] - }, - "5": { - "position": [ - -573.3045043945312, 17.407991409301758, -1646.6502685546875, 1 - ], - "rotation": [1.4982538223266602, 0, 0, 0], - "characterId": "0xc07854dc72e9e127", - "actorModelId": 49, - "health": 995833.3333333334, - "placementTime": 1770462648642, - "parentObjectCharacterId": "0xb12d863bac8922b8", - "itemDefinitionId": 148, - "slot": "PerimeterWall05", - "ownerCharacterId": "0xe9392c463df83ba4", - "passwordHash": 1308505232, - "grantedAccess": ["0xe9392c463df83ba4"] - } - }, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {}, - "permissions": { - "0xe9392c463df83ba4": { - "characterId": "0xe9392c463df83ba4", - "characterName": "808", - "useContainers": true, - "build": true, - "demolish": true, - "visit": true - } - }, - "ownerCharacterId": "0xe9392c463df83ba4", - "occupiedExpansionSlots": {}, - "occupiedRampSlots": {} - }, - "2": { - "position": [ - -584.6841430664062, 17.402490615844727, -1662.5174560546875, 1 - ], - "rotation": [0, -3.214146137237549, 0, 0], - "characterId": "0x31f884367b7e9f89", - "actorModelId": 9492, - "health": 1000000, - "placementTime": 1770822813753, - "parentObjectCharacterId": "0xb919122835f454f5", - "itemDefinitionId": 2336, - "slot": "expansion02", - "eulerAngle": -3.214146137237549, - "occupiedWallSlots": { - "1": { - "position": [ - -592.1521606445312, 17.407991409301758, -1663.1470947265625, 1 - ], - "rotation": [-6.355746269226074, 0, 0, 0], - "characterId": "0xf33476d74b24b89a", - "actorModelId": 49, - "health": 995833.3333333334, - "placementTime": 1770822817094, - "parentObjectCharacterId": "0x31f884367b7e9f89", - "itemDefinitionId": 148, - "slot": "PerimeterWall01", - "ownerCharacterId": "0xe9392c463df83ba4", - "passwordHash": 1308505232, - "grantedAccess": ["0xe9392c463df83ba4"] - }, - "2": { - "position": [ - -591.8016967773438, 17.407991409301758, -1668.0474853515625, 1 - ], - "rotation": [-1.6433461904525757, 0, 0, 0], - "characterId": "0xb04d167e5e6ab8a5", - "actorModelId": 49, - "health": 995833.3333333334, - "placementTime": 1770822818631, - "parentObjectCharacterId": "0x31f884367b7e9f89", - "itemDefinitionId": 148, - "slot": "PerimeterWall02", - "ownerCharacterId": "0xe9392c463df83ba4", - "passwordHash": 1308505232, - "grantedAccess": ["0xe9392c463df83ba4"] - }, - "3": { - "position": [ - -586.8148193359375, 17.407991409301758, -1667.68505859375, 1 - ], - "rotation": [-1.6433461904525757, 0, 0, 0], - "characterId": "0xc43e389eb432583c", - "actorModelId": 49, - "health": 995833.3333333334, - "placementTime": 1770822817884, - "parentObjectCharacterId": "0x31f884367b7e9f89", - "itemDefinitionId": 148, - "slot": "PerimeterWall03", - "ownerCharacterId": "0xe9392c463df83ba4", - "passwordHash": 1308505232, - "grantedAccess": ["0xe9392c463df83ba4"] - }, - "4": { - "position": [ - -581.8280029296875, 17.407991409301758, -1667.3226318359375, 1 - ], - "rotation": [-1.6433461904525757, 0, 0, 0], - "characterId": "0x6d5d05dc1ddf98ff", - "actorModelId": 49, - "health": 995833.3333333334, - "placementTime": 1770822819617, - "parentObjectCharacterId": "0x31f884367b7e9f89", - "itemDefinitionId": 148, - "slot": "PerimeterWall04", - "ownerCharacterId": "0xe9392c463df83ba4", - "passwordHash": 1308505232, - "grantedAccess": ["0xe9392c463df83ba4"] - }, - "5": { - "position": [ - -576.8411254882812, 17.407991409301758, -1666.9600830078125, 1 - ], - "rotation": [-3.214146137237549, 0, 0, 0], - "characterId": "0xeac252b880e87145", - "actorModelId": 49, - "health": 995833.3333333334, - "placementTime": 1770822820424, - "parentObjectCharacterId": "0x31f884367b7e9f89", - "itemDefinitionId": 148, - "slot": "PerimeterWall05", - "ownerCharacterId": "0xe9392c463df83ba4", - "passwordHash": 1308505232, - "grantedAccess": ["0xe9392c463df83ba4"] - } - }, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {}, - "permissions": { - "0xe9392c463df83ba4": { - "characterId": "0xe9392c463df83ba4", - "characterName": "808", - "useContainers": true, - "build": true, - "demolish": true, - "visit": true - } - }, - "ownerCharacterId": "0xe9392c463df83ba4", - "occupiedExpansionSlots": {}, - "occupiedRampSlots": {} - }, - "3": { - "position": [ - -592.7081298828125, 17.402490615844727, -1655.5806884765625, 1 - ], - "rotation": [0, -1.6433461904525757, 0, 0], - "characterId": "0xda4d47d9a144eb43", - "actorModelId": 9492, - "health": 1000000, - "placementTime": 1770741940060, - "parentObjectCharacterId": "0xb919122835f454f5", - "itemDefinitionId": 2336, - "slot": "expansion03", - "eulerAngle": -1.6433461904525757, - "occupiedWallSlots": { - "1": { - "position": [ - -593.3377685546875, 17.407991409301758, -1648.1126708984375, 1 - ], - "rotation": [-4.784945964813232, 0, 0, 0], - "characterId": "0xf1cffd2ab590c78a", - "actorModelId": 49, - "health": 995833.3333333334, - "placementTime": 1770741950720, - "parentObjectCharacterId": "0xda4d47d9a144eb43", - "itemDefinitionId": 148, - "slot": "PerimeterWall01", - "ownerCharacterId": "0xe9392c463df83ba4", - "passwordHash": 1308505232, - "grantedAccess": ["0xe9392c463df83ba4"] - }, - "2": { - "position": [ - -598.2381591796875, 17.407991409301758, -1648.463134765625, 1 - ], - "rotation": [-0.07254619151353836, 0, 0, 0], - "characterId": "0x02665004381ce4d9", - "actorModelId": 49, - "health": 995833.3333333334, - "placementTime": 1770741943209, - "parentObjectCharacterId": "0xda4d47d9a144eb43", - "itemDefinitionId": 148, - "slot": "PerimeterWall02", - "ownerCharacterId": "0xe9392c463df83ba4", - "passwordHash": 1308505232, - "grantedAccess": ["0xe9392c463df83ba4"] - }, - "3": { - "position": [ - -597.8756713867188, 17.407991409301758, -1653.449951171875, 1 - ], - "rotation": [-0.07254619151353836, 0, 0, 0], - "characterId": "0x81977acb10ea08f1", - "actorModelId": 49, - "health": 995833.3333333334, - "placementTime": 1770741944185, - "parentObjectCharacterId": "0xda4d47d9a144eb43", - "itemDefinitionId": 148, - "slot": "PerimeterWall03", - "ownerCharacterId": "0xe9392c463df83ba4", - "passwordHash": 1308505232, - "grantedAccess": ["0xe9392c463df83ba4"] - }, - "4": { - "position": [ - -597.5133056640625, 17.407991409301758, -1658.4368896484375, 1 - ], - "rotation": [-0.07254619151353836, 0, 0, 0], - "characterId": "0x42b9d735fb560c27", - "actorModelId": 49, - "health": 995833.3333333334, - "placementTime": 1770741945199, - "parentObjectCharacterId": "0xda4d47d9a144eb43", - "itemDefinitionId": 148, - "slot": "PerimeterWall04", - "ownerCharacterId": "0xe9392c463df83ba4", - "passwordHash": 1308505232, - "grantedAccess": ["0xe9392c463df83ba4"] - }, - "5": { - "position": [ - -597.1508178710938, 17.407991409301758, -1663.4237060546875, 1 - ], - "rotation": [-1.6433461904525757, 0, 0, 0], - "characterId": "0xdbf90c1c7ea5c46a", - "actorModelId": 49, - "health": 995833.3333333334, - "placementTime": 1770741946031, - "parentObjectCharacterId": "0xda4d47d9a144eb43", - "itemDefinitionId": 148, - "slot": "PerimeterWall05", - "ownerCharacterId": "0xe9392c463df83ba4", - "passwordHash": 1308505232, - "grantedAccess": ["0xe9392c463df83ba4"] - } - }, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {}, - "permissions": { - "0xe9392c463df83ba4": { - "characterId": "0xe9392c463df83ba4", - "characterName": "808", - "useContainers": true, - "build": true, - "demolish": true, - "visit": true - } - }, - "ownerCharacterId": "0xe9392c463df83ba4", - "occupiedExpansionSlots": {}, - "occupiedRampSlots": {} - } - }, - "occupiedRampSlots": { - "8": { - "position": [ - -577.0331420898438, 17.402490615844727, -1654.4412841796875, 1 - ], - "rotation": [0, 1.4982463121414185, 0, 0], - "characterId": "0x9550c86006af1010", - "actorModelId": 9488, - "health": 1000000, - "placementTime": 1770461447988, - "parentObjectCharacterId": "0xb919122835f454f5", - "itemDefinitionId": 2270, - "slot": "ramp08", - "eulerAngle": 1.4982463121414185, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - } - }, - "occupiedShelterSlots": { - "2": { - "position": [ - -584.8653564453125, 17.408090591430664, -1660.0240478515625, 1 - ], - "rotation": [0, 1.4982463121414185, 0, 0], - "characterId": "0x3f33bc39784ec61a", - "actorModelId": 52, - "health": 995833.3333333334, - "placementTime": 1770742916847, - "parentObjectCharacterId": "0xb919122835f454f5", - "itemDefinitionId": 153, - "slot": "Structure02", - "eulerAngle": 1.4982463121414185, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - }, - "4": { - "position": [ - -580.2410278320312, 17.408090591430664, -1654.6748046875, 1 - ], - "rotation": [0, -0.07255008816719055, 0, 0], - "characterId": "0x29679b2b13d4d090", - "actorModelId": 52, - "health": 995833.3333333334, - "placementTime": 1770742922205, - "parentObjectCharacterId": "0xb919122835f454f5", - "itemDefinitionId": 153, - "slot": "Structure04", - "eulerAngle": -0.07255008816719055, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": { - "1": { - "position": [ - -580.2410278320312, 19.9490909576416, -1654.6748046875, 1 - ], - "rotation": [0, -0.07255008816719055, 0, 0], - "characterId": "0x74a3450fe03997c5", - "actorModelId": 9408, - "health": 995833.3333333334, - "placementTime": 1770743322702, - "parentObjectCharacterId": "0x29679b2b13d4d090", - "itemDefinitionId": 1898, - "slot": "Roof01", - "eulerAngle": -0.07255008816719055, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - } - }, - "freeplaceEntities": {} - }, - "5": { - "position": [ - -585.2278442382812, 17.408090591430664, -1655.0372314453125, 1 - ], - "rotation": [0, 1.4982459545135498, 0, 0], - "characterId": "0x1ef0e117466fb133", - "actorModelId": 53, - "health": 995833.3333333334, - "placementTime": 1770743009178, - "parentObjectCharacterId": "0xb919122835f454f5", - "itemDefinitionId": 154, - "slot": "Structure05", - "eulerAngle": 1.4982459545135498, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - }, - "6": { - "position": [ - -590.2146606445312, 17.408090591430664, -1655.399658203125, 1 - ], - "rotation": [0, 3.069042682647705, 0, 0], - "characterId": "0xa734c8687365a2f7", - "actorModelId": 52, - "health": 995833.3333333334, - "placementTime": 1770393190536, - "parentObjectCharacterId": "0xb919122835f454f5", - "itemDefinitionId": 153, - "slot": "Structure06", - "eulerAngle": 3.069042682647705, - "occupiedWallSlots": { - "1": { - "position": [ - -587.7317504882812, 17.431289672851562, -1656.4140625, 1 - ], - "rotation": [3.069042682647705, 0, 0, 0], - "characterId": "0x59646060c24603f9", - "actorModelId": 9181, - "health": 497916.6666666667, - "placementTime": 1770393193862, - "parentObjectCharacterId": "0xa734c8687365a2f7", - "itemDefinitionId": 1881, - "slot": "LoveShackDoor", - "ownerCharacterId": "0xe9392c463df83ba4", - "passwordHash": 1308505232, - "grantedAccess": ["0xe9392c463df83ba4"] - } - }, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": { - "0x478e77c4018223e9": { - "position": [ - -590.3684692382812, 17.431312561035156, -1656.21435546875, 1 - ], - "rotation": [0, -1.6388804912567139, 0, 0], - "characterId": "0x478e77c4018223e9", - "actorModelId": 57, - "health": 248958.33333333334, - "placementTime": 1770459574619, - "parentObjectCharacterId": "0xa734c8687365a2f7", - "itemDefinitionId": 1982, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x3000000000045ffa", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x478e77c4018223e9", - "containerDefinitionId": 23, - "items": { - "0x3000000000006dc6": { - "itemDefinitionId": 46, - "slotId": 1, - "itemGuid": "0x3000000000006dc6", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 10, - "weapon": null - }, - "0x30000000000089c8": { - "itemDefinitionId": 16, - "slotId": 2, - "itemGuid": "0x30000000000089c8", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 39, - "weapon": null - }, - "0x30000000000032e7": { - "itemDefinitionId": 1895, - "slotId": 3, - "itemGuid": "0x30000000000032e7", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 3, - "weapon": null - }, - "0x3000000000006ae0": { - "itemDefinitionId": 2, - "slotId": 4, - "itemGuid": "0x3000000000006ae0", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 1031, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000006ae0", - "itemDefinitionId": 2, - "ammoCount": 7, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000006b84": { - "itemDefinitionId": 105, - "slotId": 5, - "itemGuid": "0x3000000000006b84", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 157, - "weapon": null - }, - "0x3000000000042c1b": { - "itemDefinitionId": 1467, - "slotId": 6, - "itemGuid": "0x3000000000042c1b", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 7, - "weapon": null - }, - "0x300000000003f461": { - "itemDefinitionId": 155, - "slotId": 7, - "itemGuid": "0x300000000003f461", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 2, - "weapon": null - }, - "0x300000000003f5ea": { - "itemDefinitionId": 23, - "slotId": 8, - "itemGuid": "0x300000000003f5ea", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 15, - "weapon": null - }, - "0x3000000000006bbc": { - "itemDefinitionId": 1903, - "slotId": 9, - "itemGuid": "0x3000000000006bbc", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000006bbc", - "itemDefinitionId": 1903, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000004734c": { - "itemDefinitionId": 3, - "slotId": 10, - "itemGuid": "0x300000000004734c", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 824, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000004734c", - "itemDefinitionId": 3, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000040da8": { - "itemDefinitionId": 142, - "slotId": 11, - "itemGuid": "0x3000000000040da8", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 2, - "weapon": null - }, - "0x30000000000473f1": { - "itemDefinitionId": 1696, - "slotId": 12, - "itemGuid": "0x30000000000473f1", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000000db62": { - "itemDefinitionId": 1696, - "slotId": 13, - "itemGuid": "0x300000000000db62", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000000473f2": { - "itemDefinitionId": 1731, - "slotId": 14, - "itemGuid": "0x30000000000473f2", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000004737c": { - "itemDefinitionId": 2, - "slotId": 15, - "itemGuid": "0x300000000004737c", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 299, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000004737c", - "itemDefinitionId": 2, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000000475f8": { - "itemDefinitionId": 1997, - "slotId": 16, - "itemGuid": "0x30000000000475f8", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 592, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000000475f8", - "itemDefinitionId": 1997, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000004910a": { - "itemDefinitionId": 14, - "slotId": 17, - "itemGuid": "0x300000000004910a", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000004910a", - "itemDefinitionId": 14, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000011cb03": { - "itemDefinitionId": 1725, - "slotId": 18, - "itemGuid": "0x300000000011cb03", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000011cb03", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000040671": { - "itemDefinitionId": 3460, - "slotId": 19, - "itemGuid": "0x3000000000040671", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000011cb25": { - "itemDefinitionId": 111, - "slotId": 20, - "itemGuid": "0x300000000011cb25", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000011cb24": { - "itemDefinitionId": 109, - "slotId": 21, - "itemGuid": "0x300000000011cb24", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000046e99": { - "itemDefinitionId": 1701, - "slotId": 22, - "itemGuid": "0x3000000000046e99", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000011d7c7": { - "itemDefinitionId": 1535, - "slotId": 23, - "itemGuid": "0x300000000011d7c7", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000011d7c3": { - "itemDefinitionId": 56, - "slotId": 24, - "itemGuid": "0x300000000011d7c3", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 3, - "weapon": null - }, - "0x300000000011dd0d": { - "itemDefinitionId": 1353, - "slotId": 25, - "itemGuid": "0x300000000011dd0d", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000011dd25": { - "itemDefinitionId": 114, - "slotId": 26, - "itemGuid": "0x300000000011dd25", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000006abf": { - "itemDefinitionId": 1374, - "slotId": 27, - "itemGuid": "0x3000000000006abf", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 1439, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000006abf", - "itemDefinitionId": 1374, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000160c39": { - "itemDefinitionId": 1725, - "slotId": 28, - "itemGuid": "0x3000000000160c39", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000160c39", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000006b0b": { - "itemDefinitionId": 1373, - "slotId": 29, - "itemGuid": "0x3000000000006b0b", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000006b0b", - "itemDefinitionId": 1373, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000000490fb": { - "itemDefinitionId": 2246, - "slotId": 30, - "itemGuid": "0x30000000000490fb", - "containerGuid": "0x3000000000045ffa", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000000490fb", - "itemDefinitionId": 2246, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "" - }, - "0x4cc9c8a9ef32e8f5": { - "position": [ - -589.2288208007812, 17.431312561035156, -1653.4814453125, 1 - ], - "rotation": [0, -0.06280123442411423, 0, 0], - "characterId": "0x4cc9c8a9ef32e8f5", - "actorModelId": 10065, - "health": 497916.6666666667, - "placementTime": 1770462454809, - "parentObjectCharacterId": "0xa734c8687365a2f7", - "itemDefinitionId": 3778, - "slot": "", - "eulerAngle": -0.06280123442411423, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - }, - "0xb908084c3438195f": { - "position": [ - -592.2112426757812, 17.431310653686523, -1653.984130859375, 1 - ], - "rotation": [0, -1.663371205329895, 0, 0], - "characterId": "0xb908084c3438195f", - "actorModelId": 9205, - "health": 248958.33333333334, - "placementTime": 1770462461039, - "parentObjectCharacterId": "0xa734c8687365a2f7", - "itemDefinitionId": 1447, - "slot": "", - "container": { - "itemDefinitionId": 1477, - "slotId": 31, - "itemGuid": "0x300000000004877c", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0xb908084c3438195f", - "containerDefinitionId": 48, - "items": { - "0x3000000000045fff": { - "itemDefinitionId": 1458, - "slotId": 1, - "itemGuid": "0x3000000000045fff", - "containerGuid": "0x300000000004877c", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 6, - "weapon": null - }, - "0x3000000000043838": { - "itemDefinitionId": 1742, - "slotId": 2, - "itemGuid": "0x3000000000043838", - "containerGuid": "0x300000000004877c", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000000473dd": { - "itemDefinitionId": 1460, - "slotId": 3, - "itemGuid": "0x30000000000473dd", - "containerGuid": "0x300000000004877c", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 6, - "weapon": null - }, - "0x3000000000045ffd": { - "itemDefinitionId": 1459, - "slotId": 4, - "itemGuid": "0x3000000000045ffd", - "containerGuid": "0x300000000004877c", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 6, - "weapon": null - }, - "0x3000000000045ffe": { - "itemDefinitionId": 1457, - "slotId": 5, - "itemGuid": "0x3000000000045ffe", - "containerGuid": "0x300000000004877c", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 6, - "weapon": null - }, - "0x3000000000048781": { - "itemDefinitionId": 1371, - "slotId": 6, - "itemGuid": "0x3000000000048781", - "containerGuid": "0x300000000004877c", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 24, - "weapon": null - }, - "0x300000000004ac92": { - "itemDefinitionId": 112, - "slotId": 7, - "itemGuid": "0x300000000004ac92", - "containerGuid": "0x300000000004877c", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 19, - "weapon": null - }, - "0x300000000011cb14": { - "itemDefinitionId": 55, - "slotId": 8, - "itemGuid": "0x300000000011cb14", - "containerGuid": "0x300000000004877c", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000011cb19": { - "itemDefinitionId": 1353, - "slotId": 9, - "itemGuid": "0x300000000011cb19", - "containerGuid": "0x300000000004877c", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "SmeltingEntity" - }, - "0x467da7c622016ab5": { - "position": [ - -590.7955322265625, 17.431310653686523, -1653.7508544921875, 1 - ], - "rotation": [0, 1.56256103515625, 0, 0], - "characterId": "0x467da7c622016ab5", - "actorModelId": 36, - "health": 248958.33333333334, - "placementTime": 1770741272944, - "parentObjectCharacterId": "0xa734c8687365a2f7", - "itemDefinitionId": 64, - "slot": "", - "container": { - "itemDefinitionId": 1711, - "slotId": 31, - "itemGuid": "0x300000000011cb0e", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x467da7c622016ab5", - "containerDefinitionId": 17, - "items": {}, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "SmeltingEntity" - } - } - }, - "8": { - "position": [ - -585.5902709960938, 17.408090591430664, -1650.0504150390625, 1 - ], - "rotation": [0, -1.6433454751968384, 0, 0], - "characterId": "0xd451a72f4067d675", - "actorModelId": 52, - "health": 995833.3333333334, - "placementTime": 1770743311864, - "parentObjectCharacterId": "0xb919122835f454f5", - "itemDefinitionId": 153, - "slot": "Structure08", - "eulerAngle": -1.6433454751968384, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - } - }, - "occupiedUpperWallSlots": {}, - "occupiedWallSlots": { - "1": { - "position": [ - -592.1644287109375, 17.408090591430664, -1663.0611572265625, 1 - ], - "rotation": [-1.6433461904525757, 0, 0, 0], - "characterId": "0x7e7ef91e84d32749", - "actorModelId": 49, - "health": 995833.3333333334, - "placementTime": 1770393176324, - "parentObjectCharacterId": "0xb919122835f454f5", - "itemDefinitionId": 148, - "slot": "PerimeterWall01", - "ownerCharacterId": "0xe9392c463df83ba4", - "passwordHash": 1308505232, - "grantedAccess": ["0xe9392c463df83ba4"] - }, - "2": { - "position": [ - -587.1776123046875, 17.408090591430664, -1662.69873046875, 1 - ], - "rotation": [-1.6433461904525757, 0, 0, 0], - "characterId": "0x2435f9b457fb8d06", - "actorModelId": 49, - "health": 995833.3333333334, - "placementTime": 1770393177842, - "parentObjectCharacterId": "0xb919122835f454f5", - "itemDefinitionId": 148, - "slot": "PerimeterWall02", - "ownerCharacterId": "0xe9392c463df83ba4", - "passwordHash": 1308505232, - "grantedAccess": ["0xe9392c463df83ba4"] - }, - "3": { - "position": [ - -582.1907348632812, 17.408090591430664, -1662.3363037109375, 1 - ], - "rotation": [-1.6433461904525757, 0, 0, 0], - "characterId": "0xfba277178163b581", - "actorModelId": 49, - "health": 995833.3333333334, - "placementTime": 1770393178434, - "parentObjectCharacterId": "0xb919122835f454f5", - "itemDefinitionId": 148, - "slot": "PerimeterWall03", - "ownerCharacterId": "0xe9392c463df83ba4", - "passwordHash": 1308505232, - "grantedAccess": ["0xe9392c463df83ba4"] - }, - "4": { - "position": [ - -577.2039184570312, 17.408090591430664, -1661.973876953125, 1 - ], - "rotation": [-3.214146137237549, 0, 0, 0], - "characterId": "0x5565f53918b76059", - "actorModelId": 49, - "health": 995833.3333333334, - "placementTime": 1770393179040, - "parentObjectCharacterId": "0xb919122835f454f5", - "itemDefinitionId": 148, - "slot": "PerimeterWall04", - "ownerCharacterId": "0xe9392c463df83ba4", - "passwordHash": 1308505232, - "grantedAccess": ["0xe9392c463df83ba4"] - }, - "5": { - "position": [ - -577.5662841796875, 17.408090591430664, -1656.987060546875, 1 - ], - "rotation": [-3.214146137237549, 0, 0, 0], - "characterId": "0x0f53b4c071afdfb8", - "actorModelId": 49, - "health": 995833.3333333334, - "placementTime": 1770393182889, - "parentObjectCharacterId": "0xb919122835f454f5", - "itemDefinitionId": 148, - "slot": "PerimeterWall05", - "ownerCharacterId": "0xe9392c463df83ba4", - "passwordHash": 1308505232, - "grantedAccess": ["0xe9392c463df83ba4"] - }, - "6": { - "position": [ - -577.9287719726562, 17.408090591430664, -1652.0001220703125, 1 - ], - "rotation": [-3.214146137237549, 0, 0, 0], - "characterId": "0xa18bd58071cd8b19", - "actorModelId": 49, - "health": 995833.3333333334, - "placementTime": 1770393179722, - "parentObjectCharacterId": "0xb919122835f454f5", - "itemDefinitionId": 148, - "slot": "PerimeterWall06", - "ownerCharacterId": "0xe9392c463df83ba4", - "passwordHash": 1308505232, - "grantedAccess": ["0xe9392c463df83ba4"] - }, - "7": { - "position": [ - -578.2911987304688, 17.408090591430664, -1647.0133056640625, 1 - ], - "rotation": [1.4982538223266602, 0, 0, 0], - "characterId": "0xe40741c0ed5ece4a", - "actorModelId": 49, - "health": 995833.3333333334, - "placementTime": 1770393186105, - "parentObjectCharacterId": "0xb919122835f454f5", - "itemDefinitionId": 148, - "slot": "PerimeterWall07", - "ownerCharacterId": "0xe9392c463df83ba4", - "passwordHash": 1308505232, - "grantedAccess": ["0xe9392c463df83ba4"] - }, - "8": { - "position": [ - -583.2780151367188, 17.408090591430664, -1647.375732421875, 1 - ], - "rotation": [1.4982538223266602, 0, 0, 0], - "characterId": "0x7dbb194889ff78b0", - "actorModelId": 49, - "health": 995833.3333333334, - "placementTime": 1770393183781, - "parentObjectCharacterId": "0xb919122835f454f5", - "itemDefinitionId": 148, - "slot": "PerimeterWall08", - "ownerCharacterId": "0xe9392c463df83ba4", - "passwordHash": 1308505232, - "grantedAccess": ["0xe9392c463df83ba4"] - }, - "9": { - "position": [ - -588.264892578125, 17.408090591430664, -1647.7381591796875, 1 - ], - "rotation": [1.4982538223266602, 0, 0, 0], - "characterId": "0x3cee11eae3001852", - "actorModelId": 49, - "health": 995833.3333333334, - "placementTime": 1770393184661, - "parentObjectCharacterId": "0xb919122835f454f5", - "itemDefinitionId": 148, - "slot": "PerimeterWall09", - "ownerCharacterId": "0xe9392c463df83ba4", - "passwordHash": 1308505232, - "grantedAccess": ["0xe9392c463df83ba4"] - }, - "10": { - "position": [ - -593.251708984375, 17.408090591430664, -1648.1005859375, 1 - ], - "rotation": [-0.07254619151353836, 0, 0, 0], - "characterId": "0x7fa8740f3315b6e1", - "actorModelId": 49, - "health": 995833.3333333334, - "placementTime": 1770393329258, - "parentObjectCharacterId": "0xb919122835f454f5", - "itemDefinitionId": 148, - "slot": "PerimeterWall10", - "ownerCharacterId": "0xe9392c463df83ba4", - "passwordHash": 1308505232, - "grantedAccess": ["0xe9392c463df83ba4"] - }, - "11": { - "position": [ - -592.8892822265625, 17.408090591430664, -1653.08740234375, 1 - ], - "rotation": [-0.07254619151353836, 0, 0, 0], - "characterId": "0x8b214000fad868b1", - "actorModelId": 49, - "health": 995833.3333333334, - "placementTime": 1770393174764, - "parentObjectCharacterId": "0xb919122835f454f5", - "itemDefinitionId": 148, - "slot": "PerimeterWall11", - "ownerCharacterId": "0xe9392c463df83ba4", - "passwordHash": 1308505232, - "grantedAccess": ["0xe9392c463df83ba4"] - }, - "12": { - "position": [ - -592.5269165039062, 17.408090591430664, -1658.0743408203125, 1 - ], - "rotation": [-0.07254619151353836, 0, 0, 0], - "characterId": "0xf66ee385f576e4ea", - "actorModelId": 49, - "health": 995833.3333333334, - "placementTime": 1770393177148, - "parentObjectCharacterId": "0xb919122835f454f5", - "itemDefinitionId": 148, - "slot": "PerimeterWall12", - "ownerCharacterId": "0xe9392c463df83ba4", - "passwordHash": 1308505232, - "grantedAccess": ["0xe9392c463df83ba4"] - } - }, - "ownerCharacterId": "0xe9392c463df83ba4", - "parentObjectCharacterId": "0x0000000000000000", - "permissions": { - "0xe9392c463df83ba4": { - "characterId": "0xe9392c463df83ba4", - "characterName": "808", - "useContainers": true, - "build": true, - "demolish": true, - "visit": true - } - }, - "placementTime": 1770392558720, - "position": [-585.217041015625, 15.27389144897461, -1655.195556640625, 1], - "rotation": [0, -1.6433461904525757, 0, 0], - "slot": "" - }, - { - "_id": { - "$oid": "69875b59194d07e49dc31d03" - }, - "characterId": "0x631dee84530d4a67", - "actorModelId": 9130, - "eulerAngle": -1.4533175230026245, - "freeplaceEntities": { - "0x792c5f83c5543fd9": { - "position": [ - -699.33056640625, 28.753984451293945, -1393.97802734375, 1 - ], - "rotation": [0, -1.4533175230026245, 0, 0], - "characterId": "0x792c5f83c5543fd9", - "actorModelId": 9411, - "health": 983333.3333333335, - "placementTime": 1770481887009, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 1897, - "slot": "Roof01", - "eulerAngle": -1.4533175230026245, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - }, - "0x5e564dfee5b9454a": { - "position": [ - -709.8511352539062, 28.753984451293945, -1397.8009033203125, 1 - ], - "rotation": [0, 0.11747877299785614, 0, 0], - "characterId": "0x5e564dfee5b9454a", - "actorModelId": 9411, - "health": 983333.3333333335, - "placementTime": 1770481898161, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 1897, - "slot": "Roof02", - "eulerAngle": 0.11747877299785614, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - }, - "0xca5b8798a4f7a195": { - "position": [ - -705.7192993164062, 26.225284576416016, -1395.6314697265625, 1 - ], - "rotation": [-1.4533175230026245, 0, 0, 0], - "characterId": "0xca5b8798a4f7a195", - "actorModelId": 9181, - "health": 491666.66666666674, - "placementTime": 1770971598135, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 1881, - "slot": "LoveShackDoor", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 1343382333, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "0x28838780a3d15e37": { - "position": [ - -702.445068359375, 26.2070369720459, -1396.154541015625, 1 - ], - "rotation": [0, 0.11417213827371597, 0, 0], - "characterId": "0x28838780a3d15e37", - "actorModelId": 9428, - "health": 245833.33333333337, - "placementTime": 1770971616483, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 2034, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x30000000001cd6a2", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x28838780a3d15e37", - "containerDefinitionId": 23, - "items": { - "0x30000000001e865f": { - "itemDefinitionId": 2203, - "slotId": 0, - "itemGuid": "0x30000000001e865f", - "containerGuid": "0x30000000001cd6a2", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 5, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [2203, 1353], - "isMutable": true - }, - "subEntityType": "CollectingEntity" - }, - "0x5a0897267454417e": { - "position": [ - -704.2420654296875, 26.20703887939453, -1395.889404296875, 1 - ], - "rotation": [0, 0.12414822727441788, 0, 0], - "characterId": "0x5a0897267454417e", - "actorModelId": 9428, - "health": 245833.33333333337, - "placementTime": 1770971621398, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 2034, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x30000000001cd6a3", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x5a0897267454417e", - "containerDefinitionId": 23, - "items": { - "0x30000000001e8660": { - "itemDefinitionId": 2203, - "slotId": 0, - "itemGuid": "0x30000000001e8660", - "containerGuid": "0x30000000001cd6a3", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 5, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [2203, 1353], - "isMutable": true - }, - "subEntityType": "CollectingEntity" - }, - "0xb03d4bb42b8aa2c4": { - "position": [ - -703.3579711914062, 26.20703887939453, -1395.98974609375, 1 - ], - "rotation": [0, 0.18683351576328278, 0, 0], - "characterId": "0xb03d4bb42b8aa2c4", - "actorModelId": 9428, - "health": 245833.33333333337, - "placementTime": 1770971624022, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 2034, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x30000000001cd6a4", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0xb03d4bb42b8aa2c4", - "containerDefinitionId": 23, - "items": { - "0x30000000001e8661": { - "itemDefinitionId": 2203, - "slotId": 0, - "itemGuid": "0x30000000001e8661", - "containerGuid": "0x30000000001cd6a4", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 5, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [2203, 1353], - "isMutable": true - }, - "subEntityType": "CollectingEntity" - }, - "0x8c8f8118a8a2980d": { - "position": [-706.34521484375, 26.20703887939453, -1395.52734375, 1], - "rotation": [0, 0.13579216599464417, 0, 0], - "characterId": "0x8c8f8118a8a2980d", - "actorModelId": 9428, - "health": 245833.33333333337, - "placementTime": 1770971628086, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 2034, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x30000000001cd6a5", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x8c8f8118a8a2980d", - "containerDefinitionId": 23, - "items": { - "0x30000000001e8662": { - "itemDefinitionId": 2203, - "slotId": 1, - "itemGuid": "0x30000000001e8662", - "containerGuid": "0x30000000001cd6a5", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 5, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [2203, 1353], - "isMutable": true - }, - "subEntityType": "CollectingEntity" - }, - "0xe8ba5a1886a8e9b6": { - "position": [ - -705.1798706054688, 26.20703887939453, -1395.88818359375, 1 - ], - "rotation": [0, -2.993145227432251, 0, 0], - "characterId": "0xe8ba5a1886a8e9b6", - "actorModelId": 9456, - "health": 9833.333333333336, - "placementTime": 1770971637210, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 51, - "slot": "", - "eulerAngle": -2.993145227432251, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - }, - "0x45cc09d698fc3f43": { - "position": [ - -704.0450439453125, 26.225284576416016, -1401.08349609375, 1 - ], - "rotation": [1.6882749795913696, 0, 0, 0], - "characterId": "0x45cc09d698fc3f43", - "actorModelId": 9181, - "health": 491666.66666666674, - "placementTime": 1770971777118, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 1881, - "slot": "LoveShackDoor", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 2136893580, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "0x8814adf7042d144c": { - "position": [ - -705.534912109375, 26.20703887939453, -1400.87744140625, 1 - ], - "rotation": [0, 0.10801079124212265, 0, 0], - "characterId": "0x8814adf7042d144c", - "actorModelId": 9428, - "health": 245833.33333333337, - "placementTime": 1770971793322, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 2034, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x30000000001cd6c4", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x8814adf7042d144c", - "containerDefinitionId": 23, - "items": { - "0x30000000001e8663": { - "itemDefinitionId": 2203, - "slotId": 0, - "itemGuid": "0x30000000001e8663", - "containerGuid": "0x30000000001cd6c4", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 5, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [2203, 1353], - "isMutable": true - }, - "subEntityType": "CollectingEntity" - }, - "0x86edc69e8e093c01": { - "position": [ - -707.1978759765625, 26.20703887939453, -1400.80615234375, 1 - ], - "rotation": [0, 0.06078805401921272, 0, 0], - "characterId": "0x86edc69e8e093c01", - "actorModelId": 9428, - "health": 245833.33333333337, - "placementTime": 1770971796486, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 2034, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x30000000001cd6c5", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x86edc69e8e093c01", - "containerDefinitionId": 23, - "items": { - "0x30000000001e8664": { - "itemDefinitionId": 2203, - "slotId": 0, - "itemGuid": "0x30000000001e8664", - "containerGuid": "0x30000000001cd6c5", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 5, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [2203, 1353], - "isMutable": true - }, - "subEntityType": "CollectingEntity" - }, - "0xd8e6a0aee3c4d518": { - "position": [ - -706.362060546875, 26.20703887939453, -1400.8226318359375, 1 - ], - "rotation": [0, 0.10779061168432236, 0, 0], - "characterId": "0xd8e6a0aee3c4d518", - "actorModelId": 9428, - "health": 245833.33333333337, - "placementTime": 1770971798506, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 2034, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x30000000001cd6c6", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0xd8e6a0aee3c4d518", - "containerDefinitionId": 23, - "items": { - "0x30000000001e8665": { - "itemDefinitionId": 2203, - "slotId": 0, - "itemGuid": "0x30000000001e8665", - "containerGuid": "0x30000000001cd6c6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 5, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [2203, 1353], - "isMutable": true - }, - "subEntityType": "CollectingEntity" - }, - "0x9642c2e400290a76": { - "position": [ - -703.340087890625, 26.207040786743164, -1401.0908203125, 1 - ], - "rotation": [0, 0.049006298184394836, 0, 0], - "characterId": "0x9642c2e400290a76", - "actorModelId": 9428, - "health": 245833.33333333337, - "placementTime": 1770971802018, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 2034, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x30000000001cd6c7", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x9642c2e400290a76", - "containerDefinitionId": 23, - "items": { - "0x30000000001e8666": { - "itemDefinitionId": 2203, - "slotId": 0, - "itemGuid": "0x30000000001e8666", - "containerGuid": "0x30000000001cd6c7", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 5, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [2203, 1353], - "isMutable": true - }, - "subEntityType": "CollectingEntity" - } - }, - "health": 1000000, - "itemDefinitionId": 1378, - "occupiedExpansionSlots": { - "1": { - "position": [-697.4335327148438, 26.20738410949707, -1399.236328125, 1], - "rotation": [0, 1.6882824897766113, 0, 0], - "characterId": "0xdb8fa1e1e8a73702", - "actorModelId": 9492, - "health": 1000000, - "placementTime": 1770478001941, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 2336, - "slot": "expansion01", - "eulerAngle": 1.6882824897766113, - "occupiedWallSlots": { - "1": { - "position": [ - -698.2258911132812, 26.2128849029541, -1406.6888427734375, 1 - ], - "rotation": [-1.4533175230026245, 0, 0, 0], - "characterId": "0xced385200594d95b", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770478297580, - "parentObjectCharacterId": "0xdb8fa1e1e8a73702", - "itemDefinitionId": 148, - "slot": "PerimeterWall01", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 2208127738, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "2": { - "position": [ - -693.3475341796875, 26.2128849029541, -1407.2703857421875, 1 - ], - "rotation": [3.259082555770874, 0, 0, 0], - "characterId": "0xf29df624752161d9", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770478299644, - "parentObjectCharacterId": "0xdb8fa1e1e8a73702", - "itemDefinitionId": 148, - "slot": "PerimeterWall02", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 767786072, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "3": { - "position": [ - -692.761474609375, 26.2128849029541, -1402.3048095703125, 1 - ], - "rotation": [3.259082555770874, 0, 0, 0], - "characterId": "0x7600f3e8c7bbd88e", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770478300408, - "parentObjectCharacterId": "0xdb8fa1e1e8a73702", - "itemDefinitionId": 148, - "slot": "PerimeterWall03", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 2569575451, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "4": { - "position": [ - -692.1753540039062, 26.2128849029541, -1397.3392333984375, 1 - ], - "rotation": [3.259082555770874, 0, 0, 0], - "characterId": "0x3b01559b33d0e61e", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770478301084, - "parentObjectCharacterId": "0xdb8fa1e1e8a73702", - "itemDefinitionId": 148, - "slot": "PerimeterWall04", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 1123387699, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "5": { - "position": [ - -691.5892944335938, 26.2128849029541, -1392.373779296875, 1 - ], - "rotation": [1.6882824897766113, 0, 0, 0], - "characterId": "0xd45487085d9887bb", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770478302408, - "parentObjectCharacterId": "0xdb8fa1e1e8a73702", - "itemDefinitionId": 148, - "slot": "PerimeterWall05", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 2073446907, - "grantedAccess": ["0x2c0f6971a76a9b46"] - } - }, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": { - "1": { - "position": [ - -695.5374145507812, 26.2128849029541, -1404.49462890625, 1 - ], - "rotation": [0, 1.6882820129394531, 0, 0], - "characterId": "0x651b0b3f93213688", - "actorModelId": 51, - "health": 983333.3333333335, - "placementTime": 1770478671429, - "parentObjectCharacterId": "0xdb8fa1e1e8a73702", - "itemDefinitionId": 150, - "slot": "Structure01", - "eulerAngle": 1.6882820129394531, - "occupiedWallSlots": { - "1": { - "position": [ - -694.1142578125, 26.22518539428711, -1402.255126953125, 1 - ], - "rotation": [1.6882820129394531, 0, 0, 0], - "characterId": "0x3de416498cbe373e", - "actorModelId": 9181, - "health": 491666.66666666674, - "placementTime": 1770478674868, - "parentObjectCharacterId": "0x651b0b3f93213688", - "itemDefinitionId": 1881, - "slot": "LoveShackDoor", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 112804768, - "grantedAccess": ["0x2c0f6971a76a9b46"] - } - }, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": { - "1": { - "position": [ - -695.5374145507812, 28.75388526916504, -1404.49462890625, 1 - ], - "rotation": [0, 1.6882820129394531, 0, 0], - "characterId": "0x1775d382c84b5a36", - "actorModelId": 9408, - "health": 983333.3333333335, - "placementTime": 1770480588810, - "parentObjectCharacterId": "0x651b0b3f93213688", - "itemDefinitionId": 1898, - "slot": "Roof01", - "eulerAngle": 1.6882820129394531, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - } - }, - "freeplaceEntities": {} - }, - "3": { - "position": [-694.365234375, 26.2128849029541, -1394.5634765625, 1], - "rotation": [0, -3.024106740951538, 0, 0], - "characterId": "0x3b2faf1a141bca7b", - "actorModelId": 52, - "health": 983333.3333333335, - "placementTime": 1770479224759, - "parentObjectCharacterId": "0xdb8fa1e1e8a73702", - "itemDefinitionId": 153, - "slot": "Structure03", - "eulerAngle": -3.024106740951538, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": { - "1": { - "position": [ - -694.365234375, 28.75388526916504, -1394.5634765625, 1 - ], - "rotation": [0, -3.024106740951538, 0, 0], - "characterId": "0x1718e18c028b5bc1", - "actorModelId": 9411, - "health": 983333.3333333335, - "placementTime": 1770480585959, - "parentObjectCharacterId": "0x3b2faf1a141bca7b", - "itemDefinitionId": 1897, - "slot": "Roof01", - "eulerAngle": -3.024106740951538, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - } - }, - "freeplaceEntities": {} - } - }, - "freeplaceEntities": {}, - "permissions": { - "0x2c0f6971a76a9b46": { - "characterId": "0x2c0f6971a76a9b46", - "characterName": "Ulio", - "useContainers": true, - "build": true, - "demolish": true, - "visit": true - } - }, - "ownerCharacterId": "0x2c0f6971a76a9b46", - "occupiedExpansionSlots": {}, - "occupiedRampSlots": {} - }, - "2": { - "position": [ - -705.76123046875, 26.20738410949707, -1405.8057861328125, 1 - ], - "rotation": [0, -3.0241174697875977, 0, 0], - "characterId": "0x013c727744207d40", - "actorModelId": 9492, - "health": 1000000, - "placementTime": 1770478018548, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 2336, - "slot": "expansion02", - "eulerAngle": -3.0241174697875977, - "occupiedWallSlots": { - "1": { - "position": [ - -713.2137451171875, 26.2128849029541, -1405.0135498046875, 1 - ], - "rotation": [-6.165717601776123, 0, 0, 0], - "characterId": "0x843ca6d6d317e6a4", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770478039840, - "parentObjectCharacterId": "0x013c727744207d40", - "itemDefinitionId": 148, - "slot": "PerimeterWall01", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 2403479199, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "2": { - "position": [ - -713.7952270507812, 26.2128849029541, -1409.891845703125, 1 - ], - "rotation": [-1.4533175230026245, 0, 0, 0], - "characterId": "0x5c7d944fe6495094", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770478039104, - "parentObjectCharacterId": "0x013c727744207d40", - "itemDefinitionId": 148, - "slot": "PerimeterWall02", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 3030246942, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "3": { - "position": [ - -708.8296508789062, 26.2128849029541, -1410.4779052734375, 1 - ], - "rotation": [-1.4533175230026245, 0, 0, 0], - "characterId": "0xb3ca35c020762926", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770478037641, - "parentObjectCharacterId": "0x013c727744207d40", - "itemDefinitionId": 148, - "slot": "PerimeterWall03", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 3731016135, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "4": { - "position": [ - -703.8641357421875, 26.2128849029541, -1411.06396484375, 1 - ], - "rotation": [-1.4533175230026245, 0, 0, 0], - "characterId": "0xe094088813ad16cc", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770478036248, - "parentObjectCharacterId": "0x013c727744207d40", - "itemDefinitionId": 148, - "slot": "PerimeterWall04", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 3886974482, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "5": { - "position": [ - -698.8985595703125, 26.2128849029541, -1411.64990234375, 1 - ], - "rotation": [-3.0241174697875977, 0, 0, 0], - "characterId": "0x753c47422ead9cd8", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770478035125, - "parentObjectCharacterId": "0x013c727744207d40", - "itemDefinitionId": 148, - "slot": "PerimeterWall05", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 4104842339, - "grantedAccess": ["0x2c0f6971a76a9b46"] - } - }, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": { - "1": { - "position": [ - -711.0194702148438, 26.2128849029541, -1407.701904296875, 1 - ], - "rotation": [0, -3.0241177082061768, 0, 0], - "characterId": "0xe9738ca173854219", - "actorModelId": 51, - "health": 983333.3333333335, - "placementTime": 1770481768685, - "parentObjectCharacterId": "0x013c727744207d40", - "itemDefinitionId": 150, - "slot": "Structure01", - "eulerAngle": -3.0241177082061768, - "occupiedWallSlots": { - "1": { - "position": [ - -708.780029296875, 26.22518539428711, -1409.125, 1 - ], - "rotation": [-3.0241177082061768, 0, 0, 0], - "characterId": "0xeeadc554df9e68c5", - "actorModelId": 9181, - "health": 491666.66666666674, - "placementTime": 1770535374692, - "parentObjectCharacterId": "0xe9738ca173854219", - "itemDefinitionId": 1881, - "slot": "LoveShackDoor", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 3597540430, - "grantedAccess": ["0x2c0f6971a76a9b46"] - } - }, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": { - "1": { - "position": [ - -711.0194702148438, 28.75388526916504, -1407.701904296875, 1 - ], - "rotation": [0, -3.0241177082061768, 0, 0], - "characterId": "0xaaf7a8d6279c9e33", - "actorModelId": 9408, - "health": 983333.3333333335, - "placementTime": 1770796367626, - "parentObjectCharacterId": "0xe9738ca173854219", - "itemDefinitionId": 1898, - "slot": "Roof01", - "eulerAngle": -3.0241177082061768, - "occupiedWallSlots": { - "1": { - "position": [ - -708.780029296875, 28.766185760498047, -1409.125, 1 - ], - "rotation": [-3.0241177082061768, 0, 0, 0], - "characterId": "0x82281fed4b42d6e1", - "actorModelId": 9181, - "health": 491666.66666666674, - "placementTime": 1770796370602, - "parentObjectCharacterId": "0xaaf7a8d6279c9e33", - "itemDefinitionId": 1881, - "slot": "LoveShackDoor", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 1874737851, - "grantedAccess": ["0x2c0f6971a76a9b46"] - } - }, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - } - }, - "freeplaceEntities": {} - }, - "2": { - "position": [ - -706.053955078125, 26.2128849029541, -1408.2879638671875, 1 - ], - "rotation": [0, 0.1174752488732338, 0, 0], - "characterId": "0x098ebf728102de5a", - "actorModelId": 53, - "health": 983333.3333333335, - "placementTime": 1770480472455, - "parentObjectCharacterId": "0x013c727744207d40", - "itemDefinitionId": 154, - "slot": "Structure02", - "eulerAngle": 0.1174752488732338, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - }, - "3": { - "position": [ - -701.0883178710938, 26.2128849029541, -1408.8740234375, 1 - ], - "rotation": [0, 0.11747520416975021, 0, 0], - "characterId": "0xa897a06c9160debc", - "actorModelId": 51, - "health": 983333.3333333335, - "placementTime": 1770481773302, - "parentObjectCharacterId": "0x013c727744207d40", - "itemDefinitionId": 150, - "slot": "Structure03", - "eulerAngle": 0.11747520416975021, - "occupiedWallSlots": { - "1": { - "position": [ - -703.3277587890625, 26.22518539428711, -1407.450927734375, 1 - ], - "rotation": [0.11747520416975021, 0, 0, 0], - "characterId": "0x020bf1128e462a3f", - "actorModelId": 9181, - "health": 491666.66666666674, - "placementTime": 1770535375664, - "parentObjectCharacterId": "0xa897a06c9160debc", - "itemDefinitionId": 1881, - "slot": "LoveShackDoor", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 2999807641, - "grantedAccess": ["0x2c0f6971a76a9b46"] - } - }, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": { - "1": { - "position": [ - -701.0883178710938, 28.75388526916504, -1408.8740234375, 1 - ], - "rotation": [0, 0.1174750030040741, 0, 0], - "characterId": "0x6f20273f213fbc70", - "actorModelId": 9408, - "health": 983333.3333333335, - "placementTime": 1770481787018, - "parentObjectCharacterId": "0xa897a06c9160debc", - "itemDefinitionId": 1898, - "slot": "Roof01", - "eulerAngle": 0.1174750030040741, - "occupiedWallSlots": { - "1": { - "position": [ - -703.3277587890625, 28.766185760498047, - -1407.450927734375, 1 - ], - "rotation": [0.1174750030040741, 0, 0, 0], - "characterId": "0x38a6dba5ecc5f60a", - "actorModelId": 9181, - "health": 491666.66666666674, - "placementTime": 1770535391143, - "parentObjectCharacterId": "0x6f20273f213fbc70", - "itemDefinitionId": 1881, - "slot": "LoveShackDoor", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 163490743, - "grantedAccess": ["0x2c0f6971a76a9b46"] - } - }, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - } - }, - "freeplaceEntities": {} - } - }, - "freeplaceEntities": { - "0x1f8a8612d515fdee": { - "position": [ - -706.053955078125, 28.75388526916504, -1408.2879638671875, 1 - ], - "rotation": [0, 0.11747514456510544, 0, 0], - "characterId": "0x1f8a8612d515fdee", - "actorModelId": 53, - "health": 983333.3333333335, - "placementTime": 1770480452686, - "parentObjectCharacterId": "0x013c727744207d40", - "itemDefinitionId": 1900, - "slot": "Roof01", - "eulerAngle": 0.11747514456510544, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - }, - "0x1d3bec9c1f6fa5bb": { - "position": [-708.780029296875, 28.766185760498047, -1409.125, 1], - "rotation": [-3.0241177082061768, 0, 0, 0], - "characterId": "0x1d3bec9c1f6fa5bb", - "actorModelId": 9181, - "health": 491666.66666666674, - "placementTime": 1770535392580, - "parentObjectCharacterId": "0x013c727744207d40", - "itemDefinitionId": 1881, - "slot": "LoveShackDoor", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 2112722131, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "0x8f7d8ad3151f9bcf": { - "position": [ - -704.0551147460938, 26.20749282836914, -1406.731201171875, 1 - ], - "rotation": [0, -3.0366971492767334, 0, 0], - "characterId": "0x8f7d8ad3151f9bcf", - "actorModelId": 57, - "health": 245833.33333333337, - "placementTime": 1771073263474, - "parentObjectCharacterId": "0x013c727744207d40", - "itemDefinitionId": 1982, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x300000000021f8db", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x8f7d8ad3151f9bcf", - "containerDefinitionId": 23, - "items": { - "0x30000000001a0bdd": { - "itemDefinitionId": 1469, - "slotId": 1, - "itemGuid": "0x30000000001a0bdd", - "containerGuid": "0x300000000021f8db", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 18, - "weapon": null - }, - "0x30000000001881f2": { - "itemDefinitionId": 1511, - "slotId": 2, - "itemGuid": "0x30000000001881f2", - "containerGuid": "0x300000000021f8db", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 26, - "weapon": null - }, - "0x30000000001cd6c3": { - "itemDefinitionId": 150, - "slotId": 3, - "itemGuid": "0x30000000001cd6c3", - "containerGuid": "0x300000000021f8db", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "" - } - }, - "permissions": { - "0x2c0f6971a76a9b46": { - "characterId": "0x2c0f6971a76a9b46", - "characterName": "Ulio", - "useContainers": true, - "build": true, - "demolish": true, - "visit": true - } - }, - "ownerCharacterId": "0x2c0f6971a76a9b46", - "occupiedExpansionSlots": {}, - "occupiedRampSlots": {} - }, - "3": { - "position": [ - -712.3305053710938, 26.20738410949707, -1397.4781494140625, 1 - ], - "rotation": [0, -1.4533175230026245, 0, 0], - "characterId": "0x27fc3f6f365df38c", - "actorModelId": 9492, - "health": 1000000, - "placementTime": 1770478011740, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 2336, - "slot": "expansion03", - "eulerAngle": -1.4533175230026245, - "occupiedWallSlots": { - "1": { - "position": [ - -711.5382080078125, 26.2128849029541, -1390.025634765625, 1 - ], - "rotation": [-4.594917297363281, 0, 0, 0], - "characterId": "0x06ea2999e75b8d76", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770478045912, - "parentObjectCharacterId": "0x27fc3f6f365df38c", - "itemDefinitionId": 148, - "slot": "PerimeterWall01", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 1510375549, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "2": { - "position": [ - -716.4165649414062, 26.2128849029541, -1389.444091796875, 1 - ], - "rotation": [0.11748247593641281, 0, 0, 0], - "characterId": "0xfe54350fc788f7e0", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770478045240, - "parentObjectCharacterId": "0x27fc3f6f365df38c", - "itemDefinitionId": 148, - "slot": "PerimeterWall02", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 142081897, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "3": { - "position": [ - -717.0025634765625, 26.2128849029541, -1394.40966796875, 1 - ], - "rotation": [0.11748247593641281, 0, 0, 0], - "characterId": "0x7f96579a884cb2e7", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770478043652, - "parentObjectCharacterId": "0x27fc3f6f365df38c", - "itemDefinitionId": 148, - "slot": "PerimeterWall03", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 414790903, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "4": { - "position": [ - -717.588623046875, 26.2128849029541, -1399.375244140625, 1 - ], - "rotation": [0.11748247593641281, 0, 0, 0], - "characterId": "0xf93bf4059533e5b7", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770478042488, - "parentObjectCharacterId": "0x27fc3f6f365df38c", - "itemDefinitionId": 148, - "slot": "PerimeterWall04", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 1630832797, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "5": { - "position": [ - -718.1746826171875, 26.2128849029541, -1404.3408203125, 1 - ], - "rotation": [-1.4533175230026245, 0, 0, 0], - "characterId": "0xa2b37a607bfeb903", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770478041272, - "parentObjectCharacterId": "0x27fc3f6f365df38c", - "itemDefinitionId": 148, - "slot": "PerimeterWall05", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 3469475806, - "grantedAccess": ["0x2c0f6971a76a9b46"] - } - }, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": { - "2": { - "position": [ - -714.8126831054688, 26.2128849029541, -1397.1854248046875, 1 - ], - "rotation": [0, 0.11747879534959793, 0, 0], - "characterId": "0xef1bbfff3f8c3b61", - "actorModelId": 52, - "health": 983333.3333333335, - "placementTime": 1770479447511, - "parentObjectCharacterId": "0x27fc3f6f365df38c", - "itemDefinitionId": 153, - "slot": "Structure02", - "eulerAngle": 0.11747879534959793, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": { - "1": { - "position": [ - -714.8126831054688, 28.75388526916504, -1397.1854248046875, 1 - ], - "rotation": [0, 0.11747877299785614, 0, 0], - "characterId": "0xb57a1345c5f66c2d", - "actorModelId": 9411, - "health": 983333.3333333335, - "placementTime": 1770480545373, - "parentObjectCharacterId": "0xef1bbfff3f8c3b61", - "itemDefinitionId": 1897, - "slot": "Roof01", - "eulerAngle": 0.11747877299785614, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - } - }, - "freeplaceEntities": {} - }, - "3": { - "position": [ - -715.3988037109375, 26.2128849029541, -1402.1510009765625, 1 - ], - "rotation": [0, -3.024113655090332, 0, 0], - "characterId": "0x83fcc0db666f5998", - "actorModelId": 51, - "health": 983333.3333333335, - "placementTime": 1770480378888, - "parentObjectCharacterId": "0x27fc3f6f365df38c", - "itemDefinitionId": 150, - "slot": "Structure03", - "eulerAngle": -3.024113655090332, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": { - "1": { - "position": [ - -715.3988037109375, 28.75388526916504, -1402.1510009765625, 1 - ], - "rotation": [0, -3.024113655090332, 0, 0], - "characterId": "0x2cea2b7fdffb2d13", - "actorModelId": 9408, - "health": 983333.3333333335, - "placementTime": 1770480549020, - "parentObjectCharacterId": "0x83fcc0db666f5998", - "itemDefinitionId": 1898, - "slot": "Roof01", - "eulerAngle": -3.024113655090332, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - } - }, - "freeplaceEntities": {} - } - }, - "freeplaceEntities": {}, - "permissions": { - "0x2c0f6971a76a9b46": { - "characterId": "0x2c0f6971a76a9b46", - "characterName": "Ulio", - "useContainers": true, - "build": true, - "demolish": true, - "visit": true - } - }, - "ownerCharacterId": "0x2c0f6971a76a9b46", - "occupiedExpansionSlots": {}, - "occupiedRampSlots": {} - }, - "4": { - "position": [ - -704.0031127929688, 26.20738410949707, -1390.9091796875, 1 - ], - "rotation": [0, 0.11748247593641281, 0, 0], - "characterId": "0x0c55393d3f592427", - "actorModelId": 9492, - "health": 1000000, - "placementTime": 1770478005880, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 2336, - "slot": "expansion04", - "eulerAngle": 0.11748247593641281, - "occupiedWallSlots": { - "1": { - "position": [ - -696.5505981445312, 26.2128849029541, -1391.7015380859375, 1 - ], - "rotation": [-3.0241174697875977, 0, 0, 0], - "characterId": "0xefb3ee6621340092", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770478303456, - "parentObjectCharacterId": "0x0c55393d3f592427", - "itemDefinitionId": 148, - "slot": "PerimeterWall01", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 1266220197, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "2": { - "position": [ - -695.9690551757812, 26.2128849029541, -1386.8231201171875, 1 - ], - "rotation": [1.6882824897766113, 0, 0, 0], - "characterId": "0x5930cc33cc11f33b", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770478051281, - "parentObjectCharacterId": "0x0c55393d3f592427", - "itemDefinitionId": 148, - "slot": "PerimeterWall02", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 136956997, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "3": { - "position": [ - -700.9346313476562, 26.2128849029541, -1386.2371826171875, 1 - ], - "rotation": [1.6882824897766113, 0, 0, 0], - "characterId": "0x47ac6d08005700e4", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770795662658, - "parentObjectCharacterId": "0x0c55393d3f592427", - "itemDefinitionId": 148, - "slot": "PerimeterWall03", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 3621198779, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "4": { - "position": [ - -705.9002075195312, 26.2128849029541, -1385.6510009765625, 1 - ], - "rotation": [1.6882824897766113, 0, 0, 0], - "characterId": "0xad703a6491b12f5b", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770478048512, - "parentObjectCharacterId": "0x0c55393d3f592427", - "itemDefinitionId": 148, - "slot": "PerimeterWall04", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 4109056267, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "5": { - "position": [ - -710.86572265625, 26.2128849029541, -1385.06494140625, 1 - ], - "rotation": [0.11748247593641281, 0, 0, 0], - "characterId": "0xc9b8040009c6204f", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770478047304, - "parentObjectCharacterId": "0x0c55393d3f592427", - "itemDefinitionId": 148, - "slot": "PerimeterWall05", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 2750631241, - "grantedAccess": ["0x2c0f6971a76a9b46"] - } - }, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": { - "1": { - "position": [ - -698.7448120117188, 26.2128849029541, -1389.0130615234375, 1 - ], - "rotation": [0, 0.11748246848583221, 0, 0], - "characterId": "0x16316b24ab113af0", - "actorModelId": 51, - "health": 983333.3333333335, - "placementTime": 1770480340586, - "parentObjectCharacterId": "0x0c55393d3f592427", - "itemDefinitionId": 150, - "slot": "Structure01", - "eulerAngle": 0.11748246848583221, - "occupiedWallSlots": { - "1": { - "position": [ - -700.9842529296875, 26.22518539428711, -1387.58984375, 1 - ], - "rotation": [0.11748246848583221, 0, 0, 0], - "characterId": "0xa1be6491591444f3", - "actorModelId": 9181, - "health": 491666.66666666674, - "placementTime": 1770535322984, - "parentObjectCharacterId": "0x16316b24ab113af0", - "itemDefinitionId": 1881, - "slot": "LoveShackDoor", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 2665928077, - "grantedAccess": ["0x2c0f6971a76a9b46"] - } - }, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": { - "1": { - "position": [ - -698.7448120117188, 28.75388526916504, -1389.0130615234375, 1 - ], - "rotation": [0, -1.4533140659332275, 0, 0], - "characterId": "0x82cf20209d0d5737", - "actorModelId": 9411, - "health": 983333.3333333335, - "placementTime": 1770796352606, - "parentObjectCharacterId": "0x16316b24ab113af0", - "itemDefinitionId": 1897, - "slot": "Roof01", - "eulerAngle": -1.4533140659332275, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - } - }, - "freeplaceEntities": {} - }, - "3": { - "position": [ - -708.676025390625, 26.2128849029541, -1387.8409423828125, 1 - ], - "rotation": [0, -3.0241100788116455, 0, 0], - "characterId": "0x3d5807a7cc3d47d4", - "actorModelId": 51, - "health": 983333.3333333335, - "placementTime": 1770480350083, - "parentObjectCharacterId": "0x0c55393d3f592427", - "itemDefinitionId": 150, - "slot": "Structure03", - "eulerAngle": -3.0241100788116455, - "occupiedWallSlots": { - "1": { - "position": [ - -706.4365844726562, 26.22518539428711, -1389.26416015625, 1 - ], - "rotation": [-3.0241100788116455, 0, 0, 0], - "characterId": "0xdae65342a9425a14", - "actorModelId": 9181, - "health": 491666.66666666674, - "placementTime": 1770535324156, - "parentObjectCharacterId": "0x3d5807a7cc3d47d4", - "itemDefinitionId": 1881, - "slot": "LoveShackDoor", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 3430427063, - "grantedAccess": ["0x2c0f6971a76a9b46"] - } - }, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": { - "1": { - "position": [ - -708.676025390625, 28.75388526916504, -1387.8409423828125, 1 - ], - "rotation": [0, -3.0241100788116455, 0, 0], - "characterId": "0xdb874f97907f2f53", - "actorModelId": 9408, - "health": 983333.3333333335, - "placementTime": 1770480356575, - "parentObjectCharacterId": "0x3d5807a7cc3d47d4", - "itemDefinitionId": 1898, - "slot": "Roof01", - "eulerAngle": -3.0241100788116455, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - } - }, - "freeplaceEntities": {} - } - }, - "freeplaceEntities": {}, - "permissions": { - "0x2c0f6971a76a9b46": { - "characterId": "0x2c0f6971a76a9b46", - "characterName": "Ulio", - "useContainers": true, - "build": true, - "demolish": true, - "visit": true - } - }, - "ownerCharacterId": "0x2c0f6971a76a9b46", - "occupiedExpansionSlots": {}, - "occupiedRampSlots": { - "3": { - "position": [ - -703.3359375, 26.207683563232422, -1385.2542724609375, 1 - ], - "rotation": [0, -1.4533175230026245, 0, 0], - "characterId": "0xacafe7f201cef43a", - "actorModelId": 9487, - "health": 1000000, - "placementTime": 1770479290020, - "parentObjectCharacterId": "0x0c55393d3f592427", - "itemDefinitionId": 2269, - "slot": "ramp03", - "eulerAngle": -1.4533175230026245, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - } - } - } - }, - "occupiedRampSlots": {}, - "occupiedShelterSlots": { - "1": { - "position": [ - -700.502685546875, 26.212984085083008, -1403.9090576171875, 1 - ], - "rotation": [0, 0.11747873574495316, 0, 0], - "characterId": "0x8eb626d36f556174", - "actorModelId": 52, - "health": 983333.3333333335, - "placementTime": 1770535100016, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 153, - "slot": "Structure01", - "eulerAngle": 0.11747873574495316, - "occupiedWallSlots": { - "1": { - "position": [ - -702.749267578125, 26.236183166503906, -1402.4439697265625, 1 - ], - "rotation": [0.11747873574495316, 0, 0, 0], - "characterId": "0x51f9f605b53e7f91", - "actorModelId": 9181, - "health": 491666.66666666674, - "placementTime": 1770796336038, - "parentObjectCharacterId": "0x8eb626d36f556174", - "itemDefinitionId": 1881, - "slot": "LoveShackDoor", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 409000202, - "grantedAccess": ["0x2c0f6971a76a9b46"] - } - }, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": { - "1": { - "position": [ - -700.502685546875, 28.753984451293945, -1403.9090576171875, 1 - ], - "rotation": [0, -1.4533175230026245, 0, 0], - "characterId": "0xabadace5ae49a168", - "actorModelId": 9411, - "health": 983333.3333333335, - "placementTime": 1770535366364, - "parentObjectCharacterId": "0x8eb626d36f556174", - "itemDefinitionId": 1897, - "slot": "Roof01", - "eulerAngle": -1.4533175230026245, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - }, - "2": { - "position": [ - -699.9201049804688, 28.753984451293945, -1398.9730224609375, 1 - ], - "rotation": [0, -1.4533175230026245, 0, 0], - "characterId": "0x2b39b3f25c74da5a", - "actorModelId": 9411, - "health": 983333.3333333335, - "placementTime": 1770535357908, - "parentObjectCharacterId": "0x8eb626d36f556174", - "itemDefinitionId": 1897, - "slot": "Roof02", - "eulerAngle": -1.4533175230026245, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - } - }, - "freeplaceEntities": { - "0x87371d4ea300ddaf": { - "position": [ - -698.2918701171875, 26.2362060546875, -1400.5426025390625, 1 - ], - "rotation": [0, -1.491416335105896, 0, 0], - "characterId": "0x87371d4ea300ddaf", - "actorModelId": 10065, - "health": 491666.66666666674, - "placementTime": 1770969801557, - "parentObjectCharacterId": "0x8eb626d36f556174", - "itemDefinitionId": 3778, - "slot": "", - "eulerAngle": -1.491416335105896, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - }, - "0x99b4148242cbd5f8": { - "position": [ - -697.8858642578125, 26.2362060546875, -1398.9072265625, 1 - ], - "rotation": [0, 1.6341005563735962, 0, 0], - "characterId": "0x99b4148242cbd5f8", - "actorModelId": 57, - "health": 245833.33333333337, - "placementTime": 1770969864356, - "parentObjectCharacterId": "0x8eb626d36f556174", - "itemDefinitionId": 1982, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x30000000001cc410", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x99b4148242cbd5f8", - "containerDefinitionId": 23, - "items": { - "0x30000000001cb1b9": { - "itemDefinitionId": 1429, - "slotId": 1, - "itemGuid": "0x30000000001cb1b9", - "containerGuid": "0x30000000001cc410", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 3, - "weapon": null - }, - "0x30000000001cba7b": { - "itemDefinitionId": 1428, - "slotId": 2, - "itemGuid": "0x30000000001cba7b", - "containerGuid": "0x30000000001cc410", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 11, - "weapon": null - }, - "0x30000000001cb24d": { - "itemDefinitionId": 1992, - "slotId": 3, - "itemGuid": "0x30000000001cb24d", - "containerGuid": "0x30000000001cc410", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 12, - "weapon": null - }, - "0x30000000001cb256": { - "itemDefinitionId": 1719, - "slotId": 4, - "itemGuid": "0x30000000001cb256", - "containerGuid": "0x30000000001cc410", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 6, - "weapon": null - }, - "0x30000000001cc472": { - "itemDefinitionId": 1998, - "slotId": 5, - "itemGuid": "0x30000000001cc472", - "containerGuid": "0x30000000001cc410", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "" - } - } - }, - "3": { - "position": [ - -710.4337158203125, 26.212984085083008, -1402.7369384765625, 1 - ], - "rotation": [0, -3.024113893508911, 0, 0], - "characterId": "0x3e266f9106249fba", - "actorModelId": 51, - "health": 983333.3333333335, - "placementTime": 1770535050736, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 150, - "slot": "Structure03", - "eulerAngle": -3.024113893508911, - "occupiedWallSlots": { - "1": { - "position": [ - -708.1942749023438, 26.225284576416016, -1404.1600341796875, 1 - ], - "rotation": [-3.024113893508911, 0, 0, 0], - "characterId": "0xa438e602e6e83552", - "actorModelId": 9181, - "health": 491666.66666666674, - "placementTime": 1770535115486, - "parentObjectCharacterId": "0x3e266f9106249fba", - "itemDefinitionId": 1881, - "slot": "LoveShackDoor", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 3333231881, - "grantedAccess": ["0x2c0f6971a76a9b46"] - } - }, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": { - "1": { - "position": [ - -710.4337158203125, 28.753984451293945, -1402.7369384765625, 1 - ], - "rotation": [0, -3.024113893508911, 0, 0], - "characterId": "0xcff8bc473f65ab7a", - "actorModelId": 9408, - "health": 983333.3333333335, - "placementTime": 1770535362568, - "parentObjectCharacterId": "0x3e266f9106249fba", - "itemDefinitionId": 1898, - "slot": "Roof01", - "eulerAngle": -3.024113893508911, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - } - }, - "freeplaceEntities": { - "0x0c147afd7e0c233e": { - "position": [ - -712.6654663085938, 26.2362060546875, -1404.3494873046875, 1 - ], - "rotation": [0, -1.4605693817138672, 0, 0], - "characterId": "0x0c147afd7e0c233e", - "actorModelId": 57, - "health": 245833.33333333337, - "placementTime": 1770535123725, - "parentObjectCharacterId": "0x3e266f9106249fba", - "itemDefinitionId": 1982, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x300000000007da0d", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x0c147afd7e0c233e", - "containerDefinitionId": 23, - "items": { - "0x300000000005202f": { - "itemDefinitionId": 1374, - "slotId": 1, - "itemGuid": "0x300000000005202f", - "containerGuid": "0x300000000007da0d", - "currentDurability": 728, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000005202f", - "itemDefinitionId": 1374, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000050da9": { - "itemDefinitionId": 1374, - "slotId": 2, - "itemGuid": "0x3000000000050da9", - "containerGuid": "0x300000000007da0d", - "currentDurability": 1954, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000050da9", - "itemDefinitionId": 1374, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000052955": { - "itemDefinitionId": 1895, - "slotId": 3, - "itemGuid": "0x3000000000052955", - "containerGuid": "0x300000000007da0d", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 25, - "weapon": null - }, - "0x300000000000b5dc": { - "itemDefinitionId": 3460, - "slotId": 4, - "itemGuid": "0x300000000000b5dc", - "containerGuid": "0x300000000007da0d", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000010a042": { - "itemDefinitionId": 155, - "slotId": 5, - "itemGuid": "0x300000000010a042", - "containerGuid": "0x300000000007da0d", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 39, - "weapon": null - }, - "0x300000000011b8e4": { - "itemDefinitionId": 46, - "slotId": 6, - "itemGuid": "0x300000000011b8e4", - "containerGuid": "0x300000000007da0d", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 31, - "weapon": null - }, - "0x300000000010a041": { - "itemDefinitionId": 47, - "slotId": 7, - "itemGuid": "0x300000000010a041", - "containerGuid": "0x300000000007da0d", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 7, - "weapon": null - }, - "0x3000000000109fa4": { - "itemDefinitionId": 1467, - "slotId": 8, - "itemGuid": "0x3000000000109fa4", - "containerGuid": "0x300000000007da0d", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 10, - "weapon": null - }, - "0x300000000011b047": { - "itemDefinitionId": 109, - "slotId": 9, - "itemGuid": "0x300000000011b047", - "containerGuid": "0x300000000007da0d", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 23, - "weapon": null - }, - "0x30000000001cb250": { - "itemDefinitionId": 78, - "slotId": 10, - "itemGuid": "0x30000000001cb250", - "containerGuid": "0x300000000007da0d", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 3, - "weapon": null - }, - "0x3000000000161e93": { - "itemDefinitionId": 48, - "slotId": 11, - "itemGuid": "0x3000000000161e93", - "containerGuid": "0x300000000007da0d", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 100, - "weapon": null - }, - "0x300000000011cabb": { - "itemDefinitionId": 1745, - "slotId": 12, - "itemGuid": "0x300000000011cabb", - "containerGuid": "0x300000000007da0d", - "currentDurability": 3184, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000011cabb", - "itemDefinitionId": 1745, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000015040c": { - "itemDefinitionId": 1903, - "slotId": 13, - "itemGuid": "0x300000000015040c", - "containerGuid": "0x300000000007da0d", - "currentDurability": 1500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000015040c", - "itemDefinitionId": 1903, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000161e67": { - "itemDefinitionId": 1903, - "slotId": 14, - "itemGuid": "0x3000000000161e67", - "containerGuid": "0x300000000007da0d", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000161e67", - "itemDefinitionId": 1903, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000021f39f": { - "itemDefinitionId": 1701, - "slotId": 15, - "itemGuid": "0x300000000021f39f", - "containerGuid": "0x300000000007da0d", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000002abdfd": { - "itemDefinitionId": 1745, - "slotId": 16, - "itemGuid": "0x30000000002abdfd", - "containerGuid": "0x300000000007da0d", - "currentDurability": 4000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002abdfd", - "itemDefinitionId": 1745, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000103c35": { - "itemDefinitionId": 1538, - "slotId": 17, - "itemGuid": "0x3000000000103c35", - "containerGuid": "0x300000000007da0d", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000103c35", - "itemDefinitionId": 1538, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000000cc58d": { - "itemDefinitionId": 1538, - "slotId": 18, - "itemGuid": "0x30000000000cc58d", - "containerGuid": "0x300000000007da0d", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000000cc58d", - "itemDefinitionId": 1538, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000002dc12f": { - "itemDefinitionId": 1373, - "slotId": 19, - "itemGuid": "0x30000000002dc12f", - "containerGuid": "0x300000000007da0d", - "currentDurability": 1580, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002dc12f", - "itemDefinitionId": 1373, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000011d8b1": { - "itemDefinitionId": 26, - "slotId": 20, - "itemGuid": "0x300000000011d8b1", - "containerGuid": "0x300000000007da0d", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 77, - "weapon": null - }, - "0x30000000001cc403": { - "itemDefinitionId": 23, - "slotId": 21, - "itemGuid": "0x30000000001cc403", - "containerGuid": "0x300000000007da0d", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 69, - "weapon": null - }, - "0x30000000002f5a68": { - "itemDefinitionId": 16, - "slotId": 22, - "itemGuid": "0x30000000002f5a68", - "containerGuid": "0x300000000007da0d", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 128, - "weapon": null - }, - "0x30000000002f5a6f": { - "itemDefinitionId": 1699, - "slotId": 23, - "itemGuid": "0x30000000002f5a6f", - "containerGuid": "0x300000000007da0d", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 18, - "weapon": null - }, - "0x30000000002f6013": { - "itemDefinitionId": 114, - "slotId": 24, - "itemGuid": "0x30000000002f6013", - "containerGuid": "0x300000000007da0d", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 3, - "weapon": null - }, - "0x30000000002f3fbd": { - "itemDefinitionId": 83, - "slotId": 25, - "itemGuid": "0x30000000002f3fbd", - "containerGuid": "0x300000000007da0d", - "currentDurability": 1048, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002f3fbd", - "itemDefinitionId": 83, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000011b8af": { - "itemDefinitionId": 1536, - "slotId": 26, - "itemGuid": "0x300000000011b8af", - "containerGuid": "0x300000000007da0d", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000011b8af", - "itemDefinitionId": 1536, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000000504cf": { - "itemDefinitionId": 1536, - "slotId": 27, - "itemGuid": "0x30000000000504cf", - "containerGuid": "0x300000000007da0d", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000000504cf", - "itemDefinitionId": 1536, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "" - }, - "0x46676f059572e8ee": { - "position": [ - -712.3409423828125, 26.2362060546875, -1402.4619140625, 1 - ], - "rotation": [0, -1.4798226356506348, 0, 0], - "characterId": "0x46676f059572e8ee", - "actorModelId": 9406, - "health": 491666.66666666674, - "placementTime": 1770741591669, - "parentObjectCharacterId": "0x3e266f9106249fba", - "itemDefinitionId": 1891, - "slot": "", - "eulerAngle": -1.4798226356506348, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - }, - "0xea79b2130668af2d": { - "position": [ - -711.2730712890625, 26.236207962036133, -1400.690673828125, 1 - ], - "rotation": [0, 1.6256191730499268, 0, 0], - "characterId": "0xea79b2130668af2d", - "actorModelId": 36, - "health": 245833.33333333337, - "placementTime": 1770741596353, - "parentObjectCharacterId": "0x3e266f9106249fba", - "itemDefinitionId": 64, - "slot": "", - "container": { - "itemDefinitionId": 1711, - "slotId": 31, - "itemGuid": "0x300000000011cb2d", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0xea79b2130668af2d", - "containerDefinitionId": 17, - "items": {}, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "SmeltingEntity" - }, - "0x97772ea8a5fdce71": { - "position": [ - -710.5319213867188, 26.236207962036133, -1400.7447509765625, 1 - ], - "rotation": [0, 1.6141785383224487, 0, 0], - "characterId": "0x97772ea8a5fdce71", - "actorModelId": 36, - "health": 245833.33333333337, - "placementTime": 1770741598537, - "parentObjectCharacterId": "0x3e266f9106249fba", - "itemDefinitionId": 64, - "slot": "", - "container": { - "itemDefinitionId": 1711, - "slotId": 31, - "itemGuid": "0x300000000011cb2e", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x97772ea8a5fdce71", - "containerDefinitionId": 17, - "items": {}, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "SmeltingEntity" - }, - "0x827d26058cc6b2ad": { - "position": [ - -709.8392944335938, 26.2362060546875, -1400.9161376953125, 1 - ], - "rotation": [0, 1.7263954877853394, 0, 0], - "characterId": "0x827d26058cc6b2ad", - "actorModelId": 36, - "health": 245833.33333333337, - "placementTime": 1770741601721, - "parentObjectCharacterId": "0x3e266f9106249fba", - "itemDefinitionId": 64, - "slot": "", - "container": { - "itemDefinitionId": 1711, - "slotId": 31, - "itemGuid": "0x300000000011cb2f", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x827d26058cc6b2ad", - "containerDefinitionId": 17, - "items": {}, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "SmeltingEntity" - }, - "0xe232793977293456": { - "position": [ - -708.8009033203125, 26.2362060546875, -1404.7877197265625, 1 - ], - "rotation": [0, 1.9630777835845947, 0, 0], - "characterId": "0xe232793977293456", - "actorModelId": 9205, - "health": 245833.33333333337, - "placementTime": 1770971681818, - "parentObjectCharacterId": "0x3e266f9106249fba", - "itemDefinitionId": 1447, - "slot": "", - "container": { - "itemDefinitionId": 1477, - "slotId": 31, - "itemGuid": "0x30000000001cd6ac", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0xe232793977293456", - "containerDefinitionId": 48, - "items": { - "0x300000000012d3f3": { - "itemDefinitionId": 2203, - "slotId": 1, - "itemGuid": "0x300000000012d3f3", - "containerGuid": "0x30000000001cd6ac", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 9, - "weapon": null - }, - "0x300000000005203e": { - "itemDefinitionId": 1402, - "slotId": 2, - "itemGuid": "0x300000000005203e", - "containerGuid": "0x30000000001cd6ac", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 3, - "weapon": null - }, - "0x300000000007ec4e": { - "itemDefinitionId": 3214, - "slotId": 3, - "itemGuid": "0x300000000007ec4e", - "containerGuid": "0x30000000001cd6ac", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 3, - "weapon": null - }, - "0x3000000000053b2d": { - "itemDefinitionId": 1436, - "slotId": 4, - "itemGuid": "0x3000000000053b2d", - "containerGuid": "0x30000000001cd6ac", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000002b155e": { - "itemDefinitionId": 1353, - "slotId": 5, - "itemGuid": "0x30000000002b155e", - "containerGuid": "0x30000000001cd6ac", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "SmeltingEntity" - } - } - }, - "7": { - "position": [ - -699.33056640625, 26.212984085083008, -1393.97802734375, 1 - ], - "rotation": [0, 0.1174786165356636, 0, 0], - "characterId": "0x36a2dc38ec50db35", - "actorModelId": 51, - "health": 983333.3333333335, - "placementTime": 1770535078584, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 150, - "slot": "Structure07", - "eulerAngle": 0.1174786165356636, - "occupiedWallSlots": { - "1": { - "position": [ - -701.5700073242188, 26.225284576416016, -1392.554931640625, 1 - ], - "rotation": [0.1174786165356636, 0, 0, 0], - "characterId": "0xe4d4456c97458a4b", - "actorModelId": 9181, - "health": 491666.66666666674, - "placementTime": 1770535337148, - "parentObjectCharacterId": "0x36a2dc38ec50db35", - "itemDefinitionId": 1881, - "slot": "LoveShackDoor", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 1621366264, - "grantedAccess": ["0x2c0f6971a76a9b46"] - } - }, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - }, - "9": { - "position": [ - -709.2615966796875, 26.212984085083008, -1392.805908203125, 1 - ], - "rotation": [0, -3.024113655090332, 0, 0], - "characterId": "0x762b51df7f4bd814", - "actorModelId": 52, - "health": 983333.3333333335, - "placementTime": 1770535043908, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 153, - "slot": "Structure09", - "eulerAngle": -3.024113655090332, - "occupiedWallSlots": { - "1": { - "position": [ - -707.0150146484375, 26.236183166503906, -1394.27099609375, 1 - ], - "rotation": [-3.024113655090332, 0, 0, 0], - "characterId": "0x466e39f73a0fe8b3", - "actorModelId": 9181, - "health": 491666.66666666674, - "placementTime": 1770535338236, - "parentObjectCharacterId": "0x762b51df7f4bd814", - "itemDefinitionId": 1881, - "slot": "LoveShackDoor", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 1923981114, - "grantedAccess": ["0x2c0f6971a76a9b46"] - } - }, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - } - }, - "occupiedUpperWallSlots": {}, - "occupiedWallSlots": { - "2": { - "position": [ - -708.2440185546875, 26.212984085083008, -1405.5128173828125, 1 - ], - "rotation": [-1.4533175230026245, 0, 0, 0], - "characterId": "0x267be46beb0cb6b4", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770479384596, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 148, - "slot": "PerimeterWall02", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 3354030250, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "3": { - "position": [ - -703.2784423828125, 26.212984085083008, -1406.0987548828125, 1 - ], - "rotation": [-1.4533175230026245, 0, 0, 0], - "characterId": "0x5cbe35538d068bc1", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770479302811, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 148, - "slot": "PerimeterWall03", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 671193179, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "4": { - "position": [ - -698.3129272460938, 26.212984085083008, -1406.684814453125, 1 - ], - "rotation": [-3.0241174697875977, 0, 0, 0], - "characterId": "0xa318541d95c504c8", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770478403620, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 148, - "slot": "PerimeterWall04", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 3850988997, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "5": { - "position": [ - -697.726806640625, 26.212984085083008, -1401.7193603515625, 1 - ], - "rotation": [-3.0241174697875977, 0, 0, 0], - "characterId": "0xfc2a3c4b6a7ad6e6", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770478402168, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 148, - "slot": "PerimeterWall05", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 262465198, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "6": { - "position": [ - -697.1408081054688, 26.212984085083008, -1396.7537841796875, 1 - ], - "rotation": [-3.0241174697875977, 0, 0, 0], - "characterId": "0x4e220f40dab2ec22", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770478401332, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 148, - "slot": "PerimeterWall06", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 2398626080, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "7": { - "position": [ - -696.5548095703125, 26.212984085083008, -1391.788330078125, 1 - ], - "rotation": [1.6882824897766113, 0, 0, 0], - "characterId": "0x0304f476b5b05ab7", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770478399696, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 148, - "slot": "PerimeterWall07", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 3239284137, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "8": { - "position": [ - -701.5203247070312, 26.212984085083008, -1391.2022705078125, 1 - ], - "rotation": [1.6882824897766113, 0, 0, 0], - "characterId": "0x593ed5d00720c5ee", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770795688482, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 148, - "slot": "PerimeterWall08", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 1202750449, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "9": { - "position": [ - -706.48583984375, 26.212984085083008, -1390.6160888671875, 1 - ], - "rotation": [1.6882824897766113, 0, 0, 0], - "characterId": "0x1c5f823bc73c17d5", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770478397292, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 148, - "slot": "PerimeterWall09", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 2915131833, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "10": { - "position": [ - -711.451416015625, 26.212984085083008, -1390.0301513671875, 1 - ], - "rotation": [0.11748247593641281, 0, 0, 0], - "characterId": "0x61a6fa37f7218491", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770478396660, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 148, - "slot": "PerimeterWall10", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 619150407, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "11": { - "position": [ - -712.0374145507812, 26.212984085083008, -1394.9957275390625, 1 - ], - "rotation": [0.11748247593641281, 0, 0, 0], - "characterId": "0xc2bd8cb95a2b12a7", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770478395532, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 148, - "slot": "PerimeterWall11", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 793626815, - "grantedAccess": ["0x2c0f6971a76a9b46"] - }, - "12": { - "position": [ - -712.62353515625, 26.212984085083008, -1399.961181640625, 1 - ], - "rotation": [0.11748247593641281, 0, 0, 0], - "characterId": "0x46402f32f06ba66d", - "actorModelId": 49, - "health": 983333.3333333335, - "placementTime": 1770478394904, - "parentObjectCharacterId": "0x631dee84530d4a67", - "itemDefinitionId": 148, - "slot": "PerimeterWall12", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 2423741573, - "grantedAccess": ["0x2c0f6971a76a9b46"] - } - }, - "ownerCharacterId": "0x2c0f6971a76a9b46", - "parentObjectCharacterId": "0x0000000000000000", - "permissions": { - "0x2c0f6971a76a9b46": { - "characterId": "0x2c0f6971a76a9b46", - "characterName": "Ulio", - "useContainers": true, - "build": true, - "demolish": true, - "visit": true - } - }, - "placementTime": 1770477989636, - "position": [ - -704.9014892578125, 24.078784942626953, -1398.5150146484375, 1 - ], - "rotation": [0, -1.4533175230026245, 0, 0], - "slot": "" - }, - { - "_id": { - "$oid": "698accf8194d07e49d163746" - }, - "characterId": "0x1d5e09ee82725e4f", - "actorModelId": 9130, - "eulerAngle": -1.5135596990585327, - "freeplaceEntities": { - "0x3431dd104dc90f09": { - "position": [ - -889.58642578125, 57.82255935668945, -1720.446044921875, 1 - ], - "rotation": [0, -1.5219261646270752, 0, 0], - "characterId": "0x3431dd104dc90f09", - "actorModelId": 9428, - "health": 189824.93500980228, - "placementTime": 1770800380594, - "parentObjectCharacterId": "0x1d5e09ee82725e4f", - "itemDefinitionId": 2034, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x3000000000149384", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x3431dd104dc90f09", - "containerDefinitionId": 23, - "items": { - "0x3000000000161e62": { - "itemDefinitionId": 2203, - "slotId": 1, - "itemGuid": "0x3000000000161e62", - "containerGuid": "0x3000000000149384", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 5, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [2203, 1353], - "isMutable": true - }, - "subEntityType": "CollectingEntity" - }, - "0xd314222efeee6991": { - "position": [ - -889.5015869140625, 57.82255935668945, -1719.44873046875, 1 - ], - "rotation": [0, -1.5806292295455933, 0, 0], - "characterId": "0xd314222efeee6991", - "actorModelId": 9428, - "health": 118442.82007385803, - "placementTime": 1770800386256, - "parentObjectCharacterId": "0x1d5e09ee82725e4f", - "itemDefinitionId": 2034, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x3000000000149385", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0xd314222efeee6991", - "containerDefinitionId": 23, - "items": { - "0x3000000000161e63": { - "itemDefinitionId": 2203, - "slotId": 1, - "itemGuid": "0x3000000000161e63", - "containerGuid": "0x3000000000149385", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 5, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [2203, 1353], - "isMutable": true - }, - "subEntityType": "CollectingEntity" - }, - "0x9b39d6861bd57818": { - "position": [ - -889.5247802734375, 57.82255935668945, -1718.68115234375, 1 - ], - "rotation": [0, -1.5674809217453003, 0, 0], - "characterId": "0x9b39d6861bd57818", - "actorModelId": 9428, - "health": 134726.01207027887, - "placementTime": 1770800388277, - "parentObjectCharacterId": "0x1d5e09ee82725e4f", - "itemDefinitionId": 2034, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x3000000000149386", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x9b39d6861bd57818", - "containerDefinitionId": 23, - "items": { - "0x3000000000161e64": { - "itemDefinitionId": 2203, - "slotId": 1, - "itemGuid": "0x3000000000161e64", - "containerGuid": "0x3000000000149386", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 5, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [2203, 1353], - "isMutable": true - }, - "subEntityType": "CollectingEntity" - }, - "0xc8f962f35e3c391e": { - "position": [-889.6323852539062, 60.39270782470703, -1717.0234375, 1], - "rotation": [-3.0843558311462402, 0, 0, 0], - "characterId": "0xc8f962f35e3c391e", - "actorModelId": 9181, - "health": 454166.6666666671, - "placementTime": 1770778366994, - "parentObjectCharacterId": "0x1d5e09ee82725e4f", - "itemDefinitionId": 1881, - "slot": "LoveShackDoor", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 184690225, - "grantedAccess": ["0x870613ce5e50d8e9"] - }, - "0xedd8e7531448aa1d": { - "position": [ - -890.2437133789062, 60.381805419921875, -1726.9639892578125, 1 - ], - "rotation": [-3.0843558311462402, 0, 0, 0], - "characterId": "0xedd8e7531448aa1d", - "actorModelId": 9181, - "health": 352166.6666666671, - "placementTime": 1770778533452, - "parentObjectCharacterId": "0x1d5e09ee82725e4f", - "itemDefinitionId": 1881, - "slot": "LoveShackDoor", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 857290543, - "grantedAccess": ["0x870613ce5e50d8e9"] - }, - "0xadb30452b27fb2cc": { - "position": [ - -884.6259765625, 57.851707458496094, -1719.931396484375, 1 - ], - "rotation": [0.05723664164543152, 0, 0, 0], - "characterId": "0xadb30452b27fb2cc", - "actorModelId": 9181, - "health": 454166.6666666671, - "placementTime": 1770715364503, - "parentObjectCharacterId": "0x1d5e09ee82725e4f", - "itemDefinitionId": 1881, - "slot": "LoveShackDoor", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 1138048918, - "grantedAccess": ["0x870613ce5e50d8e9"] - }, - "0x571b224bcdde9f51": { - "position": [ - -880.6417236328125, 60.310428619384766, -1714.4378662109375, 1 - ], - "rotation": [0, 0.010749280452728271, 0, 0], - "characterId": "0x571b224bcdde9f51", - "actorModelId": 9406, - "health": 131457.42021829856, - "placementTime": 1770785740880, - "parentObjectCharacterId": "0x1d5e09ee82725e4f", - "itemDefinitionId": 1891, - "slot": "", - "eulerAngle": 0.010749280452728271, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - }, - "0x388b73978e2fba6c": { - "position": [-888.328125, 57.851707458496094, -1718.311279296875, 1], - "rotation": [-1.5135595798492432, 0, 0, 0], - "characterId": "0x388b73978e2fba6c", - "actorModelId": 9181, - "health": 454166.6666666671, - "placementTime": 1770715365128, - "parentObjectCharacterId": "0x1d5e09ee82725e4f", - "itemDefinitionId": 1881, - "slot": "LoveShackDoor", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 1589214430, - "grantedAccess": ["0x870613ce5e50d8e9"] - }, - "0x10b155643c2d1819": { - "position": [ - -880.4659423828125, 57.85172653198242, -1724.5211181640625, 1 - ], - "rotation": [0, 1.605750322341919, 0, 0], - "characterId": "0x10b155643c2d1819", - "actorModelId": 57, - "health": 215625.00000000032, - "placementTime": 1770705506130, - "parentObjectCharacterId": "0x1d5e09ee82725e4f", - "itemDefinitionId": 1982, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x300000000010227c", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x10b155643c2d1819", - "containerDefinitionId": 23, - "items": { - "0x30000000000c6268": { - "itemDefinitionId": 1895, - "slotId": 1, - "itemGuid": "0x30000000000c6268", - "containerGuid": "0x300000000010227c", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 7, - "weapon": null - }, - "0x3000000000101356": { - "itemDefinitionId": 1354, - "slotId": 2, - "itemGuid": "0x3000000000101356", - "containerGuid": "0x300000000010227c", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 3, - "weapon": null - }, - "0x30000000000f4c45": { - "itemDefinitionId": 1441, - "slotId": 3, - "itemGuid": "0x30000000000f4c45", - "containerGuid": "0x300000000010227c", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 4, - "weapon": null - }, - "0x3000000000103d4f": { - "itemDefinitionId": 82, - "slotId": 4, - "itemGuid": "0x3000000000103d4f", - "containerGuid": "0x300000000010227c", - "currentDurability": 1490, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000103d4f", - "itemDefinitionId": 82, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000102b4f": { - "itemDefinitionId": 1542, - "slotId": 5, - "itemGuid": "0x3000000000102b4f", - "containerGuid": "0x300000000010227c", - "currentDurability": 1650, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000102b4f", - "itemDefinitionId": 1542, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000107bf4": { - "itemDefinitionId": 124, - "slotId": 6, - "itemGuid": "0x3000000000107bf4", - "containerGuid": "0x300000000010227c", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000109f64": { - "itemDefinitionId": 109, - "slotId": 7, - "itemGuid": "0x3000000000109f64", - "containerGuid": "0x300000000010227c", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000000db6f": { - "itemDefinitionId": 3460, - "slotId": 8, - "itemGuid": "0x300000000000db6f", - "containerGuid": "0x300000000010227c", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000138413": { - "itemDefinitionId": 124, - "slotId": 9, - "itemGuid": "0x3000000000138413", - "containerGuid": "0x300000000010227c", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000011ef35": { - "itemDefinitionId": 1467, - "slotId": 10, - "itemGuid": "0x300000000011ef35", - "containerGuid": "0x300000000010227c", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 4, - "weapon": null - }, - "0x30000000000c65c6": { - "itemDefinitionId": 3460, - "slotId": 11, - "itemGuid": "0x30000000000c65c6", - "containerGuid": "0x300000000010227c", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000000f4c48": { - "itemDefinitionId": 1403, - "slotId": 12, - "itemGuid": "0x30000000000f4c48", - "containerGuid": "0x300000000010227c", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000138d44": { - "itemDefinitionId": 14, - "slotId": 13, - "itemGuid": "0x3000000000138d44", - "containerGuid": "0x300000000010227c", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000138d44", - "itemDefinitionId": 14, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000011e062": { - "itemDefinitionId": 7, - "slotId": 14, - "itemGuid": "0x300000000011e062", - "containerGuid": "0x300000000010227c", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 2, - "weapon": null - }, - "0x3000000000103571": { - "itemDefinitionId": 3460, - "slotId": 15, - "itemGuid": "0x3000000000103571", - "containerGuid": "0x300000000010227c", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000138d64": { - "itemDefinitionId": 14, - "slotId": 16, - "itemGuid": "0x3000000000138d64", - "containerGuid": "0x300000000010227c", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000138d64", - "itemDefinitionId": 14, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000141397": { - "itemDefinitionId": 3, - "slotId": 17, - "itemGuid": "0x3000000000141397", - "containerGuid": "0x300000000010227c", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000141397", - "itemDefinitionId": 3, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000011b8ee": { - "itemDefinitionId": 9, - "slotId": 18, - "itemGuid": "0x300000000011b8ee", - "containerGuid": "0x300000000010227c", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001057a0": { - "itemDefinitionId": 1903, - "slotId": 19, - "itemGuid": "0x30000000001057a0", - "containerGuid": "0x300000000010227c", - "currentDurability": 1950, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000001057a0", - "itemDefinitionId": 1903, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000013a859": { - "itemDefinitionId": 3460, - "slotId": 20, - "itemGuid": "0x300000000013a859", - "containerGuid": "0x300000000010227c", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000000c6586": { - "itemDefinitionId": 3460, - "slotId": 21, - "itemGuid": "0x30000000000c6586", - "containerGuid": "0x300000000010227c", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "" - }, - "0x1a1d5f157c3954c0": { - "position": [ - -881.1480102539062, 57.85172653198242, -1728.1204833984375, 1 - ], - "rotation": [0, 0.033231910318136215, 0, 0], - "characterId": "0x1a1d5f157c3954c0", - "actorModelId": 9406, - "health": 431250.00000000064, - "placementTime": 1770708961202, - "parentObjectCharacterId": "0x1d5e09ee82725e4f", - "itemDefinitionId": 1891, - "slot": "", - "eulerAngle": 0.033231910318136215, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - }, - "0x8bd7fa522e115c8a": { - "position": [ - -882.32763671875, 57.85172653198242, -1728.0050048828125, 1 - ], - "rotation": [0, -1.394723653793335, 0, 0], - "characterId": "0x8bd7fa522e115c8a", - "actorModelId": 36, - "health": 215625.00000000032, - "placementTime": 1770711969949, - "parentObjectCharacterId": "0x1d5e09ee82725e4f", - "itemDefinitionId": 64, - "slot": "", - "container": { - "itemDefinitionId": 1711, - "slotId": 31, - "itemGuid": "0x3000000000107ba0", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x8bd7fa522e115c8a", - "containerDefinitionId": 17, - "items": {}, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "SmeltingEntity" - }, - "0x68ad710496ed8a1a": { - "position": [ - -880.575439453125, 60.310428619384766, -1727.9969482421875, 1 - ], - "rotation": [0, 1.6105091571807861, 0, 0], - "characterId": "0x68ad710496ed8a1a", - "actorModelId": 9406, - "health": 431250.00000000064, - "placementTime": 1770715851247, - "parentObjectCharacterId": "0x1d5e09ee82725e4f", - "itemDefinitionId": 1891, - "slot": "", - "eulerAngle": 1.6105091571807861, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - }, - "0x989d55a914a0a8c1": { - "position": [ - -883.2797241210938, 57.85172653198242, -1727.925537109375, 1 - ], - "rotation": [0, -1.497941017150879, 0, 0], - "characterId": "0x989d55a914a0a8c1", - "actorModelId": 36, - "health": 215625.00000000032, - "placementTime": 1770791933053, - "parentObjectCharacterId": "0x1d5e09ee82725e4f", - "itemDefinitionId": 64, - "slot": "", - "container": { - "itemDefinitionId": 1711, - "slotId": 31, - "itemGuid": "0x30000000001426c2", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x989d55a914a0a8c1", - "containerDefinitionId": 17, - "items": {}, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "SmeltingEntity" - }, - "0x08abcdbce61d13dd": { - "position": [ - -884.2106323242188, 57.85172653198242, -1727.68115234375, 1 - ], - "rotation": [0, -1.4644511938095093, 0, 0], - "characterId": "0x08abcdbce61d13dd", - "actorModelId": 36, - "health": 215625.00000000032, - "placementTime": 1770791935781, - "parentObjectCharacterId": "0x1d5e09ee82725e4f", - "itemDefinitionId": 64, - "slot": "", - "container": { - "itemDefinitionId": 1711, - "slotId": 31, - "itemGuid": "0x30000000001426c4", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x08abcdbce61d13dd", - "containerDefinitionId": 17, - "items": {}, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "SmeltingEntity" - } - }, - "health": 1000000, - "itemDefinitionId": 1378, - "occupiedExpansionSlots": { - "1": { - "position": [ - -879.7990112304688, 57.822906494140625, -1721.4012451171875, 1 - ], - "rotation": [0, 1.6280403137207031, 0, 0], - "characterId": "0xb511debafbe40eb5", - "actorModelId": 9492, - "health": 1000000, - "placementTime": 1770704008135, - "parentObjectCharacterId": "0x1d5e09ee82725e4f", - "itemDefinitionId": 2336, - "slot": "expansion01", - "eulerAngle": 1.6280403137207031, - "occupiedWallSlots": { - "1": { - "position": [ - -880.1412353515625, 57.828407287597656, -1728.887939453125, 1 - ], - "rotation": [-1.5135596990585327, 0, 0, 0], - "characterId": "0x5fdd3913e5f0094d", - "actorModelId": 49, - "health": 862500.0000000013, - "placementTime": 1770704019926, - "parentObjectCharacterId": "0xb511debafbe40eb5", - "itemDefinitionId": 148, - "slot": "PerimeterWall01", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 50795800, - "grantedAccess": ["0x870613ce5e50d8e9"] - }, - "2": { - "position": [ - -875.2367553710938, 57.828407287597656, -1729.1746826171875, 1 - ], - "rotation": [3.198840379714966, 0, 0, 0], - "characterId": "0xa278313bd88faeef", - "actorModelId": 49, - "health": 862500.0000000013, - "placementTime": 1770704020483, - "parentObjectCharacterId": "0xb511debafbe40eb5", - "itemDefinitionId": 148, - "slot": "PerimeterWall02", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 1013440036, - "grantedAccess": ["0x870613ce5e50d8e9"] - }, - "3": { - "position": [ - -874.95068359375, 57.828407287597656, -1724.182861328125, 1 - ], - "rotation": [3.198840379714966, 0, 0, 0], - "characterId": "0x35548d44e0942742", - "actorModelId": 49, - "health": 862500.0000000013, - "placementTime": 1770704021796, - "parentObjectCharacterId": "0xb511debafbe40eb5", - "itemDefinitionId": 148, - "slot": "PerimeterWall03", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 1013440036, - "grantedAccess": ["0x870613ce5e50d8e9"] - }, - "4": { - "position": [ - -874.6646118164062, 57.828407287597656, -1719.1910400390625, 1 - ], - "rotation": [3.198840379714966, 0, 0, 0], - "characterId": "0x6a9873ceaa740039", - "actorModelId": 49, - "health": 862500.0000000013, - "placementTime": 1770704022370, - "parentObjectCharacterId": "0xb511debafbe40eb5", - "itemDefinitionId": 148, - "slot": "PerimeterWall04", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 1013440036, - "grantedAccess": ["0x870613ce5e50d8e9"] - }, - "5": { - "position": [ - -874.3785400390625, 57.828407287597656, -1714.19921875, 1 - ], - "rotation": [1.6280403137207031, 0, 0, 0], - "characterId": "0xc79263e8cab7b56b", - "actorModelId": 49, - "health": 806295.9364471288, - "placementTime": 1770704022882, - "parentObjectCharacterId": "0xb511debafbe40eb5", - "itemDefinitionId": 148, - "slot": "PerimeterWall05", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 1013440036, - "grantedAccess": ["0x870613ce5e50d8e9"] - } - }, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": { - "1": { - "position": [ - -877.5897827148438, 57.828407287597656, -1726.5357666015625, 1 - ], - "rotation": [0, 0.057243816554546356, 0, 0], - "characterId": "0x5d98bac0b02759d1", - "actorModelId": 52, - "health": 428590.80006391165, - "placementTime": 1770711697100, - "parentObjectCharacterId": "0xb511debafbe40eb5", - "itemDefinitionId": 153, - "slot": "Structure01", - "eulerAngle": 0.057243816554546356, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": { - "1": { - "position": [ - -877.5897827148438, 60.369407653808594, -1726.5357666015625, 1 - ], - "rotation": [0, 0.057243622839450836, 0, 0], - "characterId": "0x89a3fdbf3cbb74f4", - "actorModelId": 9408, - "health": 631532.3111621358, - "placementTime": 1770712099765, - "parentObjectCharacterId": "0x5d98bac0b02759d1", - "itemDefinitionId": 1898, - "slot": "Roof01", - "eulerAngle": 0.057243622839450836, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - } - }, - "freeplaceEntities": {} - }, - "3": { - "position": [ - -877.017578125, 57.828407287597656, -1716.5521240234375, 1 - ], - "rotation": [0, 0.057243864983320236, 0, 0], - "characterId": "0x2970d8d91e0abdd9", - "actorModelId": 51, - "health": 334631.7294967767, - "placementTime": 1770711705730, - "parentObjectCharacterId": "0xb511debafbe40eb5", - "itemDefinitionId": 150, - "slot": "Structure03", - "eulerAngle": 0.057243864983320236, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - } - }, - "freeplaceEntities": { - "0x18ce57e085d43fa0": { - "position": [ - -879.6361694335938, 60.392608642578125, -1720.2464599609375, 1 - ], - "rotation": [0.057243797928094864, 0, 0, 0], - "characterId": "0x18ce57e085d43fa0", - "actorModelId": 9181, - "health": 104036.06769169706, - "placementTime": 1770715817144, - "parentObjectCharacterId": "0xb511debafbe40eb5", - "itemDefinitionId": 1435, - "slot": "LoveShackDoor", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 1500326025, - "grantedAccess": ["0x870613ce5e50d8e9"] - }, - "0xc696a654f8a226fe": { - "position": [ - -877.1583251953125, 62.8513298034668, -1717.9901123046875, 1 - ], - "rotation": [0, 0.6388015747070312, 0, 0], - "characterId": "0xc696a654f8a226fe", - "actorModelId": 55, - "health": 86249.99999999984, - "placementTime": 1770715906240, - "parentObjectCharacterId": "0xb511debafbe40eb5", - "itemDefinitionId": 97, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x3000000000109f77", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0xc696a654f8a226fe", - "containerDefinitionId": 23, - "items": {}, - "canAcceptItems": true, - "acceptedItems": [1353], - "isMutable": true - }, - "subEntityType": "CollectingEntity" - } - }, - "permissions": { - "0x870613ce5e50d8e9": { - "characterId": "0x870613ce5e50d8e9", - "characterName": "Bik", - "useContainers": true, - "build": true, - "demolish": true, - "visit": true - } - }, - "ownerCharacterId": "0x870613ce5e50d8e9", - "occupiedExpansionSlots": {}, - "occupiedRampSlots": {} - }, - "2": { - "position": [ - -887.716064453125, 57.822906494140625, -1728.460205078125, 1 - ], - "rotation": [0, -3.084359645843506, 0, 0], - "characterId": "0x5d98ae1164d7feb0", - "actorModelId": 9492, - "health": 1000000, - "placementTime": 1770704011515, - "parentObjectCharacterId": "0x1d5e09ee82725e4f", - "itemDefinitionId": 2336, - "slot": "expansion02", - "eulerAngle": -3.084359645843506, - "occupiedWallSlots": { - "1": { - "position": [ - -895.2027587890625, 57.828407287597656, -1728.1180419921875, 1 - ], - "rotation": [-6.225959777832031, 0, 0, 0], - "characterId": "0x1943a63f5c535c47", - "actorModelId": 49, - "health": 862500.0000000013, - "placementTime": 1770704039342, - "parentObjectCharacterId": "0x5d98ae1164d7feb0", - "itemDefinitionId": 148, - "slot": "PerimeterWall01", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 2809765886, - "grantedAccess": ["0x870613ce5e50d8e9"] - }, - "2": { - "position": [ - -895.489501953125, 57.828407287597656, -1733.0225830078125, 1 - ], - "rotation": [-1.5135596990585327, 0, 0, 0], - "characterId": "0xc7d95ad53d23436b", - "actorModelId": 49, - "health": 862500.0000000013, - "placementTime": 1770704037293, - "parentObjectCharacterId": "0x5d98ae1164d7feb0", - "itemDefinitionId": 148, - "slot": "PerimeterWall02", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 2884161392, - "grantedAccess": ["0x870613ce5e50d8e9"] - }, - "3": { - "position": [ - -890.4976196289062, 57.828407287597656, -1733.30859375, 1 - ], - "rotation": [-1.5135596990585327, 0, 0, 0], - "characterId": "0xf271e009f42b2382", - "actorModelId": 49, - "health": 862500.0000000013, - "placementTime": 1770704031821, - "parentObjectCharacterId": "0x5d98ae1164d7feb0", - "itemDefinitionId": 148, - "slot": "PerimeterWall03", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 1458953084, - "grantedAccess": ["0x870613ce5e50d8e9"] - }, - "4": { - "position": [ - -885.5057983398438, 57.828407287597656, -1733.5946044921875, 1 - ], - "rotation": [-1.5135596990585327, 0, 0, 0], - "characterId": "0x02901056606c4e7c", - "actorModelId": 49, - "health": 862500.0000000013, - "placementTime": 1770704028264, - "parentObjectCharacterId": "0x5d98ae1164d7feb0", - "itemDefinitionId": 148, - "slot": "PerimeterWall04", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 50795800, - "grantedAccess": ["0x870613ce5e50d8e9"] - }, - "5": { - "position": [ - -880.5139770507812, 57.828407287597656, -1733.880615234375, 1 - ], - "rotation": [-3.084359645843506, 0, 0, 0], - "characterId": "0x9152e2c9190065dd", - "actorModelId": 49, - "health": 862500.0000000013, - "placementTime": 1770704024239, - "parentObjectCharacterId": "0x5d98ae1164d7feb0", - "itemDefinitionId": 148, - "slot": "PerimeterWall05", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 2809765886, - "grantedAccess": ["0x870613ce5e50d8e9"] - } - }, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": { - "1": { - "position": [ - -892.8506469726562, 57.828407287597656, -1730.6695556640625, 1 - ], - "rotation": [0, 1.6280299425125122, 0, 0], - "characterId": "0x7e19c32d85023149", - "actorModelId": 52, - "health": 680384.8297456143, - "placementTime": 1770708915645, - "parentObjectCharacterId": "0x5d98ae1164d7feb0", - "itemDefinitionId": 153, - "slot": "Structure01", - "eulerAngle": 1.6280299425125122, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": { - "1": { - "position": [ - -892.8506469726562, 60.369407653808594, -1730.6695556640625, 1 - ], - "rotation": [0, 1.6280299425125122, 0, 0], - "characterId": "0x1a0c487c7c624087", - "actorModelId": 9408, - "health": 830962.5121340188, - "placementTime": 1770715020852, - "parentObjectCharacterId": "0x7e19c32d85023149", - "itemDefinitionId": 1898, - "slot": "Roof01", - "eulerAngle": 1.6280299425125122, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - }, - "2": { - "position": [ - -887.8884887695312, 60.369407653808594, -1730.953857421875, 1 - ], - "rotation": [0, 1.6280299425125122, 0, 0], - "characterId": "0xcaf0303cc8838d36", - "actorModelId": 9411, - "health": 236108.91873021962, - "placementTime": 1770711940189, - "parentObjectCharacterId": "0x7e19c32d85023149", - "itemDefinitionId": 1897, - "slot": "Roof02", - "eulerAngle": 1.6280299425125122, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - } - }, - "freeplaceEntities": {} - }, - "3": { - "position": [ - -882.8668823242188, 57.828407287597656, -1731.2415771484375, 1 - ], - "rotation": [0, 0.057233039289712906, 0, 0], - "characterId": "0x2a79ca854ff56b00", - "actorModelId": 51, - "health": 862500.0000000013, - "placementTime": 1770708842390, - "parentObjectCharacterId": "0x5d98ae1164d7feb0", - "itemDefinitionId": 150, - "slot": "Structure03", - "eulerAngle": 0.057233039289712906, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - } - }, - "freeplaceEntities": {}, - "permissions": { - "0x870613ce5e50d8e9": { - "characterId": "0x870613ce5e50d8e9", - "characterName": "Bik", - "useContainers": true, - "build": true, - "demolish": true, - "visit": true - } - }, - "ownerCharacterId": "0x870613ce5e50d8e9", - "occupiedExpansionSlots": {}, - "occupiedRampSlots": {} - }, - "3": { - "position": [ - -894.7747802734375, 57.822906494140625, -1720.543212890625, 1 - ], - "rotation": [0, -1.5135596990585327, 0, 0], - "characterId": "0x56fa410771c3b4f8", - "actorModelId": 9492, - "health": 1000000, - "placementTime": 1770704010561, - "parentObjectCharacterId": "0x1d5e09ee82725e4f", - "itemDefinitionId": 2336, - "slot": "expansion03", - "eulerAngle": -1.5135596990585327, - "occupiedWallSlots": { - "2": { - "position": [ - -899.3370971679688, 57.828407287597656, -1712.769775390625, 1 - ], - "rotation": [0.05724029988050461, 0, 0, 0], - "characterId": "0x87ce3b74de3598dc", - "actorModelId": 49, - "health": 590500.0000000013, - "placementTime": 1770705177832, - "parentObjectCharacterId": "0x56fa410771c3b4f8", - "itemDefinitionId": 148, - "slot": "PerimeterWall02", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 50795800, - "grantedAccess": ["0x870613ce5e50d8e9"] - }, - "4": { - "position": [ - -899.9091796875, 57.828407287597656, -1722.75341796875, 1 - ], - "rotation": [0.05724029988050461, 0, 0, 0], - "characterId": "0xa4ba49c18b8b0557", - "actorModelId": 49, - "health": 556500.0000000013, - "placementTime": 1770705179301, - "parentObjectCharacterId": "0x56fa410771c3b4f8", - "itemDefinitionId": 148, - "slot": "PerimeterWall04", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 50795800, - "grantedAccess": ["0x870613ce5e50d8e9"] - }, - "5": { - "position": [ - -900.1951904296875, 57.828407287597656, -1727.7452392578125, 1 - ], - "rotation": [-1.5135596990585327, 0, 0, 0], - "characterId": "0x74b2444e8f64949b", - "actorModelId": 49, - "health": 862500.0000000013, - "placementTime": 1770705179805, - "parentObjectCharacterId": "0x56fa410771c3b4f8", - "itemDefinitionId": 148, - "slot": "PerimeterWall05", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 287547375, - "grantedAccess": ["0x870613ce5e50d8e9"] - } - }, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": { - "1": { - "position": [ - -896.9840698242188, 57.828407287597656, -1715.40869140625, 1 - ], - "rotation": [0, -1.5135594606399536, 0, 0], - "characterId": "0xbb827aa04de6d8e5", - "actorModelId": 51, - "health": 371410.25605773873, - "placementTime": 1770708755432, - "parentObjectCharacterId": "0x56fa410771c3b4f8", - "itemDefinitionId": 150, - "slot": "Structure01", - "eulerAngle": -1.5135594606399536, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - }, - "3": { - "position": [ - -897.55615234375, 57.828407287597656, -1725.392333984375, 1 - ], - "rotation": [0, 1.62803316116333, 0, 0], - "characterId": "0x082a254f6f2d875d", - "actorModelId": 51, - "health": 381466.8531968046, - "placementTime": 1770708758273, - "parentObjectCharacterId": "0x56fa410771c3b4f8", - "itemDefinitionId": 150, - "slot": "Structure03", - "eulerAngle": 1.62803316116333, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": { - "1": { - "position": [ - -897.55615234375, 60.369407653808594, -1725.392333984375, 1 - ], - "rotation": [0, -3.0843558311462402, 0, 0], - "characterId": "0x3d8b9ee3a569fb4d", - "actorModelId": 9408, - "health": 414075.4681664542, - "placementTime": 1770712278849, - "parentObjectCharacterId": "0x082a254f6f2d875d", - "itemDefinitionId": 1898, - "slot": "Roof01", - "eulerAngle": -3.0843558311462402, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - } - }, - "freeplaceEntities": {} - } - }, - "freeplaceEntities": { - "0xb07430ef2511e307": { - "position": [ - -894.6533203125, 60.392608642578125, -1716.73583984375, 1 - ], - "rotation": [-3.0843558311462402, 0, 0, 0], - "characterId": "0xb07430ef2511e307", - "actorModelId": 9181, - "health": 454166.6666666671, - "placementTime": 1770712406684, - "parentObjectCharacterId": "0x56fa410771c3b4f8", - "itemDefinitionId": 1881, - "slot": "LoveShackDoor", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 4200858801, - "grantedAccess": ["0x870613ce5e50d8e9"] - } - }, - "permissions": { - "0x870613ce5e50d8e9": { - "characterId": "0x870613ce5e50d8e9", - "characterName": "Bik", - "useContainers": true, - "build": true, - "demolish": true, - "visit": true - } - }, - "ownerCharacterId": "0x870613ce5e50d8e9", - "occupiedExpansionSlots": {}, - "occupiedRampSlots": { - "3": { - "position": [ - -900.4595947265625, 57.82320785522461, -1720.2177734375, 1 - ], - "rotation": [0, -3.084359645843506, 0, 0], - "characterId": "0x7d33f516f95696cc", - "actorModelId": 9487, - "health": 1000000, - "placementTime": 1770704079300, - "parentObjectCharacterId": "0x56fa410771c3b4f8", - "itemDefinitionId": 2269, - "slot": "ramp03", - "eulerAngle": -3.084359645843506, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - } - } - }, - "4": { - "position": [ - -886.8579711914062, 57.822906494140625, -1713.4847412109375, 1 - ], - "rotation": [0, 0.05724029988050461, 0, 0], - "characterId": "0x0a12d532f790e8f8", - "actorModelId": 9492, - "health": 1000000, - "placementTime": 1770704009481, - "parentObjectCharacterId": "0x1d5e09ee82725e4f", - "itemDefinitionId": 2336, - "slot": "expansion04", - "eulerAngle": 0.05724029988050461, - "occupiedWallSlots": { - "1": { - "position": [ - -879.3712768554688, 57.828407287597656, -1713.826904296875, 1 - ], - "rotation": [-3.084359645843506, 0, 0, 0], - "characterId": "0xd19b4c6e5d66f79f", - "actorModelId": 49, - "health": 862500.0000000013, - "placementTime": 1770705172938, - "parentObjectCharacterId": "0x0a12d532f790e8f8", - "itemDefinitionId": 148, - "slot": "PerimeterWall01", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 2809765886, - "grantedAccess": ["0x870613ce5e50d8e9"] - }, - "2": { - "position": [ - -879.0845336914062, 57.828407287597656, -1708.9224853515625, 1 - ], - "rotation": [1.6280403137207031, 0, 0, 0], - "characterId": "0x86d5cba2a7c33fb3", - "actorModelId": 49, - "health": 862500.0000000013, - "placementTime": 1770705174181, - "parentObjectCharacterId": "0x0a12d532f790e8f8", - "itemDefinitionId": 148, - "slot": "PerimeterWall02", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 1458953084, - "grantedAccess": ["0x870613ce5e50d8e9"] - }, - "3": { - "position": [ - -884.0763549804688, 57.828407287597656, -1708.636474609375, 1 - ], - "rotation": [1.6280403137207031, 0, 0, 0], - "characterId": "0xa9450d8f72b86f62", - "actorModelId": 49, - "health": 862500.0000000013, - "placementTime": 1770705175971, - "parentObjectCharacterId": "0x0a12d532f790e8f8", - "itemDefinitionId": 148, - "slot": "PerimeterWall03", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 3512331793, - "grantedAccess": ["0x870613ce5e50d8e9"] - }, - "4": { - "position": [ - -889.0681762695312, 57.828407287597656, -1708.350341796875, 1 - ], - "rotation": [1.6280403137207031, 0, 0, 0], - "characterId": "0xb9b128d97523ea2b", - "actorModelId": 49, - "health": 862500.0000000013, - "placementTime": 1770705174992, - "parentObjectCharacterId": "0x0a12d532f790e8f8", - "itemDefinitionId": 148, - "slot": "PerimeterWall04", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 2809765886, - "grantedAccess": ["0x870613ce5e50d8e9"] - }, - "5": { - "position": [ - -894.0599975585938, 57.828407287597656, -1708.0643310546875, 1 - ], - "rotation": [0.05724029988050461, 0, 0, 0], - "characterId": "0xc32ab340fbb1538e", - "actorModelId": 49, - "health": 862500.0000000013, - "placementTime": 1770705176745, - "parentObjectCharacterId": "0x0a12d532f790e8f8", - "itemDefinitionId": 148, - "slot": "PerimeterWall05", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 3686084936, - "grantedAccess": ["0x870613ce5e50d8e9"] - } - }, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": { - "1": { - "position": [ - -881.723388671875, 57.828407287597656, -1711.2755126953125, 1 - ], - "rotation": [0, -1.5135561227798462, 0, 0], - "characterId": "0x66e2813710249221", - "actorModelId": 52, - "health": 834448.104501046, - "placementTime": 1770708768421, - "parentObjectCharacterId": "0x0a12d532f790e8f8", - "itemDefinitionId": 153, - "slot": "Structure01", - "eulerAngle": -1.5135561227798462, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": { - "1": { - "position": [ - -881.723388671875, 60.369407653808594, -1711.2755126953125, 1 - ], - "rotation": [0, -1.5135561227798462, 0, 0], - "characterId": "0xa64c635491a20c17", - "actorModelId": 9408, - "health": 299969.3451601763, - "placementTime": 1770715063578, - "parentObjectCharacterId": "0x66e2813710249221", - "itemDefinitionId": 1898, - "slot": "Roof01", - "eulerAngle": -1.5135561227798462, - "occupiedWallSlots": { - "1": { - "position": [ - -883.0091552734375, 60.38170623779297, - -1713.5965576171875, 1 - ], - "rotation": [-1.5135561227798462, 0, 0, 0], - "characterId": "0x72e29c73d7c76fa2", - "actorModelId": 9181, - "health": 431250.00000000064, - "placementTime": 1770778396997, - "parentObjectCharacterId": "0xa64c635491a20c17", - "itemDefinitionId": 1881, - "slot": "LoveShackDoor", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 714838991, - "grantedAccess": ["0x870613ce5e50d8e9"] - } - }, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": { - "0x546205929764f73e": { - "position": [ - -880.0413818359375, 60.39262390136719, -1712.89599609375, - 1 - ], - "rotation": [0, 1.670119285583496, 0, 0], - "characterId": "0x546205929764f73e", - "actorModelId": 9406, - "health": 431250.00000000064, - "placementTime": 1770784050057, - "parentObjectCharacterId": "0xa64c635491a20c17", - "itemDefinitionId": 1891, - "slot": "", - "eulerAngle": 1.670119285583496, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - }, - "0xcd05b2b2b9106d4e": { - "position": [ - -880.035888671875, 60.39263153076172, -1710.95068359375, 1 - ], - "rotation": [0, -1.471514344215393, 0, 0], - "characterId": "0xcd05b2b2b9106d4e", - "actorModelId": 10065, - "health": 431250.00000000064, - "placementTime": 1770784062994, - "parentObjectCharacterId": "0xa64c635491a20c17", - "itemDefinitionId": 3778, - "slot": "", - "eulerAngle": -1.471514344215393, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - }, - "0xf3ecb426a5615de3": { - "position": [ - -880.4137573242188, 60.39262771606445, -1713.143798828125, - 1 - ], - "rotation": [0, 1.643853783607483, 0, 0], - "characterId": "0xf3ecb426a5615de3", - "actorModelId": 57, - "health": 215625.00000000032, - "placementTime": 1770784070206, - "parentObjectCharacterId": "0xa64c635491a20c17", - "itemDefinitionId": 1982, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x300000000013cc20", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0xf3ecb426a5615de3", - "containerDefinitionId": 23, - "items": { - "0x300000000013ade8": { - "itemDefinitionId": 1386, - "slotId": 1, - "itemGuid": "0x300000000013ade8", - "containerGuid": "0x300000000013cc20", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 10, - "weapon": null - }, - "0x3000000000107bf5": { - "itemDefinitionId": 1987, - "slotId": 2, - "itemGuid": "0x3000000000107bf5", - "containerGuid": "0x300000000013cc20", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 65, - "weapon": null - }, - "0x3000000000107bf6": { - "itemDefinitionId": 1988, - "slotId": 3, - "itemGuid": "0x3000000000107bf6", - "containerGuid": "0x300000000013cc20", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 63, - "weapon": null - }, - "0x3000000000054e4a": { - "itemDefinitionId": 1371, - "slotId": 4, - "itemGuid": "0x3000000000054e4a", - "containerGuid": "0x300000000013cc20", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 44, - "weapon": null - }, - "0x3000000000049a43": { - "itemDefinitionId": 73, - "slotId": 5, - "itemGuid": "0x3000000000049a43", - "containerGuid": "0x300000000013cc20", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 4, - "weapon": null - }, - "0x3000000000144bfd": { - "itemDefinitionId": 1699, - "slotId": 6, - "itemGuid": "0x3000000000144bfd", - "containerGuid": "0x300000000013cc20", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 34, - "weapon": null - }, - "0x3000000000136d65": { - "itemDefinitionId": 57, - "slotId": 7, - "itemGuid": "0x3000000000136d65", - "containerGuid": "0x300000000013cc20", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 22, - "weapon": null - }, - "0x3000000000168695": { - "itemDefinitionId": 25, - "slotId": 8, - "itemGuid": "0x3000000000168695", - "containerGuid": "0x300000000013cc20", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 11, - "weapon": null - }, - "0x3000000000167e28": { - "itemDefinitionId": 11, - "slotId": 9, - "itemGuid": "0x3000000000167e28", - "containerGuid": "0x300000000013cc20", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 6, - "weapon": null - }, - "0x30000000001698a8": { - "itemDefinitionId": 74, - "slotId": 10, - "itemGuid": "0x30000000001698a8", - "containerGuid": "0x300000000013cc20", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 8, - "weapon": null - }, - "0x300000000014816b": { - "itemDefinitionId": 1353, - "slotId": 11, - "itemGuid": "0x300000000014816b", - "containerGuid": "0x300000000013cc20", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 2, - "weapon": null - }, - "0x30000000001ac06f": { - "itemDefinitionId": 107, - "slotId": 12, - "itemGuid": "0x30000000001ac06f", - "containerGuid": "0x300000000013cc20", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 30, - "weapon": null - }, - "0x30000000001ac219": { - "itemDefinitionId": 1438, - "slotId": 13, - "itemGuid": "0x30000000001ac219", - "containerGuid": "0x300000000013cc20", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 30, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "" - }, - "0x42b56e9e1cfe2d29": { - "position": [ - -880.285888671875, 60.39263153076172, -1712.515869140625, - 1 - ], - "rotation": [0, 0.03972258418798447, 0, 0], - "characterId": "0x42b56e9e1cfe2d29", - "actorModelId": 57, - "health": 215625.00000000032, - "placementTime": 1770784082444, - "parentObjectCharacterId": "0xa64c635491a20c17", - "itemDefinitionId": 1982, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x300000000013cc21", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x42b56e9e1cfe2d29", - "containerDefinitionId": 23, - "items": { - "0x3000000000101057": { - "itemDefinitionId": 1459, - "slotId": 1, - "itemGuid": "0x3000000000101057", - "containerGuid": "0x300000000013cc21", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 6, - "weapon": null - }, - "0x3000000000054e46": { - "itemDefinitionId": 1460, - "slotId": 2, - "itemGuid": "0x3000000000054e46", - "containerGuid": "0x300000000013cc21", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 5, - "weapon": null - }, - "0x3000000000054e49": { - "itemDefinitionId": 1458, - "slotId": 3, - "itemGuid": "0x3000000000054e49", - "containerGuid": "0x300000000013cc21", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 5, - "weapon": null - }, - "0x3000000000054e48": { - "itemDefinitionId": 1457, - "slotId": 4, - "itemGuid": "0x3000000000054e48", - "containerGuid": "0x300000000013cc21", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 5, - "weapon": null - }, - "0x300000000013a81f": { - "itemDefinitionId": 78, - "slotId": 5, - "itemGuid": "0x300000000013a81f", - "containerGuid": "0x300000000013cc21", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 11, - "weapon": null - }, - "0x300000000013a815": { - "itemDefinitionId": 1751, - "slotId": 6, - "itemGuid": "0x300000000013a815", - "containerGuid": "0x300000000013cc21", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 33, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "" - }, - "0xb784a9f2f24ca174": { - "position": [ - -883.4805297851562, 60.39263153076172, -1709.054443359375, - 1 - ], - "rotation": [0, 0.021719949319958687, 0, 0], - "characterId": "0xb784a9f2f24ca174", - "actorModelId": 57, - "health": 215625.00000000032, - "placementTime": 1770784097256, - "parentObjectCharacterId": "0xa64c635491a20c17", - "itemDefinitionId": 1982, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x300000000013cc22", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0xb784a9f2f24ca174", - "containerDefinitionId": 23, - "items": { - "0x300000000013a81e": { - "itemDefinitionId": 1469, - "slotId": 1, - "itemGuid": "0x300000000013a81e", - "containerGuid": "0x300000000013cc22", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 56, - "weapon": null - }, - "0x300000000013a81d": { - "itemDefinitionId": 1511, - "slotId": 2, - "itemGuid": "0x300000000013a81d", - "containerGuid": "0x300000000013cc22", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 140, - "weapon": null - }, - "0x300000000013cc25": { - "itemDefinitionId": 1429, - "slotId": 3, - "itemGuid": "0x300000000013cc25", - "containerGuid": "0x300000000013cc22", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 128, - "weapon": null - }, - "0x30000000000fc7c7": { - "itemDefinitionId": 2325, - "slotId": 4, - "itemGuid": "0x30000000000fc7c7", - "containerGuid": "0x300000000013cc22", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 32, - "weapon": null - }, - "0x30000000000f42f6": { - "itemDefinitionId": 1428, - "slotId": 5, - "itemGuid": "0x30000000000f42f6", - "containerGuid": "0x300000000013cc22", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 124, - "weapon": null - }, - "0x300000000005202c": { - "itemDefinitionId": 1719, - "slotId": 6, - "itemGuid": "0x300000000005202c", - "containerGuid": "0x300000000013cc22", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 82, - "weapon": null - }, - "0x30000000000fc771": { - "itemDefinitionId": 1998, - "slotId": 7, - "itemGuid": "0x30000000000fc771", - "containerGuid": "0x300000000013cc22", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 73, - "weapon": null - }, - "0x30000000000fd050": { - "itemDefinitionId": 1992, - "slotId": 8, - "itemGuid": "0x30000000000fd050", - "containerGuid": "0x300000000013cc22", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 50, - "weapon": null - }, - "0x30000000000cbc83": { - "itemDefinitionId": 2645, - "slotId": 9, - "itemGuid": "0x30000000000cbc83", - "containerGuid": "0x300000000013cc22", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 2, - "weapon": null - }, - "0x30000000000c6cc8": { - "itemDefinitionId": 2648, - "slotId": 10, - "itemGuid": "0x30000000000c6cc8", - "containerGuid": "0x300000000013cc22", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 3, - "weapon": null - }, - "0x30000000000c6cc9": { - "itemDefinitionId": 2646, - "slotId": 11, - "itemGuid": "0x30000000000c6cc9", - "containerGuid": "0x300000000013cc22", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000000c6cca": { - "itemDefinitionId": 2647, - "slotId": 12, - "itemGuid": "0x30000000000c6cca", - "containerGuid": "0x300000000013cc22", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "" - }, - "0xe2b241f3e4b1a4bf": { - "position": [ - -882.607421875, 60.39262390136719, -1709.080322265625, 1 - ], - "rotation": [0, 0.035164617002010345, 0, 0], - "characterId": "0xe2b241f3e4b1a4bf", - "actorModelId": 57, - "health": 215625.00000000032, - "placementTime": 1770784101008, - "parentObjectCharacterId": "0xa64c635491a20c17", - "itemDefinitionId": 1982, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x300000000013cc23", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0xe2b241f3e4b1a4bf", - "containerDefinitionId": 23, - "items": { - "0x300000000013adeb": { - "itemDefinitionId": 3446, - "slotId": 1, - "itemGuid": "0x300000000013adeb", - "containerGuid": "0x300000000013cc23", - "currentDurability": 1000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000013adeb", - "itemDefinitionId": 3446, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000001033f9": { - "itemDefinitionId": 1373, - "slotId": 2, - "itemGuid": "0x30000000001033f9", - "containerGuid": "0x300000000013cc23", - "currentDurability": 1070, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000001033f9", - "itemDefinitionId": 1373, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000010723b": { - "itemDefinitionId": 2901, - "slotId": 3, - "itemGuid": "0x300000000010723b", - "containerGuid": "0x300000000013cc23", - "currentDurability": 1267, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000010723b", - "itemDefinitionId": 2901, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000010458f": { - "itemDefinitionId": 1374, - "slotId": 4, - "itemGuid": "0x300000000010458f", - "containerGuid": "0x300000000013cc23", - "currentDurability": 1588, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000010458f", - "itemDefinitionId": 1374, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000000ce49e": { - "itemDefinitionId": 10, - "slotId": 5, - "itemGuid": "0x30000000000ce49e", - "containerGuid": "0x300000000013cc23", - "currentDurability": 1535, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000000ce49e", - "itemDefinitionId": 10, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000137ac1": { - "itemDefinitionId": 1373, - "slotId": 6, - "itemGuid": "0x3000000000137ac1", - "containerGuid": "0x300000000013cc23", - "currentDurability": 888, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000137ac1", - "itemDefinitionId": 1373, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000000c6482": { - "itemDefinitionId": 10, - "slotId": 7, - "itemGuid": "0x30000000000c6482", - "containerGuid": "0x300000000013cc23", - "currentDurability": 1888, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000000c6482", - "itemDefinitionId": 10, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000137131": { - "itemDefinitionId": 10, - "slotId": 8, - "itemGuid": "0x3000000000137131", - "containerGuid": "0x300000000013cc23", - "currentDurability": 1829, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000137131", - "itemDefinitionId": 10, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000000f4be9": { - "itemDefinitionId": 2901, - "slotId": 9, - "itemGuid": "0x30000000000f4be9", - "containerGuid": "0x300000000013cc23", - "currentDurability": 1426, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000000f4be9", - "itemDefinitionId": 2901, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000013713b": { - "itemDefinitionId": 3123, - "slotId": 10, - "itemGuid": "0x300000000013713b", - "containerGuid": "0x300000000013cc23", - "currentDurability": 1310, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000013713b", - "itemDefinitionId": 3123, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000054e4e": { - "itemDefinitionId": 3446, - "slotId": 11, - "itemGuid": "0x3000000000054e4e", - "containerGuid": "0x300000000013cc23", - "currentDurability": 825, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000054e4e", - "itemDefinitionId": 3446, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000001442a8": { - "itemDefinitionId": 2901, - "slotId": 12, - "itemGuid": "0x30000000001442a8", - "containerGuid": "0x300000000013cc23", - "currentDurability": 1642, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000001442a8", - "itemDefinitionId": 2901, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000013cd32": { - "itemDefinitionId": 10, - "slotId": 13, - "itemGuid": "0x300000000013cd32", - "containerGuid": "0x300000000013cc23", - "currentDurability": 396, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000013cd32", - "itemDefinitionId": 10, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000000f3a06": { - "itemDefinitionId": 3123, - "slotId": 14, - "itemGuid": "0x30000000000f3a06", - "containerGuid": "0x300000000013cc23", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000000f3a06", - "itemDefinitionId": 3123, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000135f9d": { - "itemDefinitionId": 2901, - "slotId": 15, - "itemGuid": "0x3000000000135f9d", - "containerGuid": "0x300000000013cc23", - "currentDurability": 1410, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000135f9d", - "itemDefinitionId": 2901, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "" - }, - "0x8ad349e23a0e405b": { - "position": [ - -883.7014770507812, 60.39262771606445, - -1713.2161865234375, 1 - ], - "rotation": [0, 0.0514574870467186, 0, 0], - "characterId": "0x8ad349e23a0e405b", - "actorModelId": 57, - "health": 215625.00000000032, - "placementTime": 1770784105748, - "parentObjectCharacterId": "0xa64c635491a20c17", - "itemDefinitionId": 1982, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x300000000013cc24", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x8ad349e23a0e405b", - "containerDefinitionId": 23, - "items": { - "0x30000000000ce724": { - "itemDefinitionId": 3169, - "slotId": 1, - "itemGuid": "0x30000000000ce724", - "containerGuid": "0x300000000013cc24", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000102c20": { - "itemDefinitionId": 5050, - "slotId": 2, - "itemGuid": "0x3000000000102c20", - "containerGuid": "0x300000000013cc24", - "currentDurability": 5400, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000137ab6": { - "itemDefinitionId": 2169, - "slotId": 3, - "itemGuid": "0x3000000000137ab6", - "containerGuid": "0x300000000013cc24", - "currentDurability": 100, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000000cd380": { - "itemDefinitionId": 2545, - "slotId": 4, - "itemGuid": "0x30000000000cd380", - "containerGuid": "0x300000000013cc24", - "currentDurability": 1000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000000c9cd6": { - "itemDefinitionId": 3169, - "slotId": 5, - "itemGuid": "0x30000000000c9cd6", - "containerGuid": "0x300000000013cc24", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000000c647f": { - "itemDefinitionId": 2271, - "slotId": 6, - "itemGuid": "0x30000000000c647f", - "containerGuid": "0x300000000013cc24", - "currentDurability": 1000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000000cb922": { - "itemDefinitionId": 2271, - "slotId": 7, - "itemGuid": "0x30000000000cb922", - "containerGuid": "0x300000000013cc24", - "currentDurability": 1000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000000d097b": { - "itemDefinitionId": 2271, - "slotId": 8, - "itemGuid": "0x30000000000d097b", - "containerGuid": "0x300000000013cc24", - "currentDurability": 1000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000000d1bc1": { - "itemDefinitionId": 2271, - "slotId": 9, - "itemGuid": "0x30000000000d1bc1", - "containerGuid": "0x300000000013cc24", - "currentDurability": 1000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000107231": { - "itemDefinitionId": 2062, - "slotId": 10, - "itemGuid": "0x3000000000107231", - "containerGuid": "0x300000000013cc24", - "currentDurability": 100, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000000cbcf0": { - "itemDefinitionId": 3169, - "slotId": 11, - "itemGuid": "0x30000000000cbcf0", - "containerGuid": "0x300000000013cc24", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000013576d": { - "itemDefinitionId": 2216, - "slotId": 12, - "itemGuid": "0x300000000013576d", - "containerGuid": "0x300000000013cc24", - "currentDurability": 5400, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000138d37": { - "itemDefinitionId": 2169, - "slotId": 13, - "itemGuid": "0x3000000000138d37", - "containerGuid": "0x300000000013cc24", - "currentDurability": 100, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000000d0977": { - "itemDefinitionId": 2545, - "slotId": 14, - "itemGuid": "0x30000000000d0977", - "containerGuid": "0x300000000013cc24", - "currentDurability": 1000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000013a052": { - "itemDefinitionId": 2217, - "slotId": 15, - "itemGuid": "0x300000000013a052", - "containerGuid": "0x300000000013cc24", - "currentDurability": 5400, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000104f3b": { - "itemDefinitionId": 2218, - "slotId": 16, - "itemGuid": "0x3000000000104f3b", - "containerGuid": "0x300000000013cc24", - "currentDurability": 5400, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000000ce49f": { - "itemDefinitionId": 3169, - "slotId": 17, - "itemGuid": "0x30000000000ce49f", - "containerGuid": "0x300000000013cc24", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000000ca239": { - "itemDefinitionId": 2545, - "slotId": 18, - "itemGuid": "0x30000000000ca239", - "containerGuid": "0x300000000013cc24", - "currentDurability": 1000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000138d45": { - "itemDefinitionId": 2271, - "slotId": 19, - "itemGuid": "0x3000000000138d45", - "containerGuid": "0x300000000013cc24", - "currentDurability": 1000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000137abb": { - "itemDefinitionId": 2170, - "slotId": 20, - "itemGuid": "0x3000000000137abb", - "containerGuid": "0x300000000013cc24", - "currentDurability": 100, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000000c65a7": { - "itemDefinitionId": 3460, - "slotId": 21, - "itemGuid": "0x30000000000c65a7", - "containerGuid": "0x300000000013cc24", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000142fe6": { - "itemDefinitionId": 2172, - "slotId": 22, - "itemGuid": "0x3000000000142fe6", - "containerGuid": "0x300000000013cc24", - "currentDurability": 100, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000107240": { - "itemDefinitionId": 2148, - "slotId": 23, - "itemGuid": "0x3000000000107240", - "containerGuid": "0x300000000013cc24", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000013b2b1": { - "itemDefinitionId": 2217, - "slotId": 24, - "itemGuid": "0x300000000013b2b1", - "containerGuid": "0x300000000013cc24", - "currentDurability": 5400, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001466ca": { - "itemDefinitionId": 2216, - "slotId": 25, - "itemGuid": "0x30000000001466ca", - "containerGuid": "0x300000000013cc24", - "currentDurability": 5400, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000138d3b": { - "itemDefinitionId": 2148, - "slotId": 26, - "itemGuid": "0x3000000000138d3b", - "containerGuid": "0x300000000013cc24", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000151814": { - "itemDefinitionId": 2271, - "slotId": 27, - "itemGuid": "0x3000000000151814", - "containerGuid": "0x300000000013cc24", - "currentDurability": 1000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000151817": { - "itemDefinitionId": 2271, - "slotId": 28, - "itemGuid": "0x3000000000151817", - "containerGuid": "0x300000000013cc24", - "currentDurability": 1000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000152f41": { - "itemDefinitionId": 2124, - "slotId": 29, - "itemGuid": "0x3000000000152f41", - "containerGuid": "0x300000000013cc24", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000152f3d": { - "itemDefinitionId": 2124, - "slotId": 30, - "itemGuid": "0x3000000000152f3d", - "containerGuid": "0x300000000013cc24", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "" - }, - "0xf0835cb45669d7a2": { - "position": [ - -880.990478515625, 60.39262771606445, -1713.37255859375, 1 - ], - "rotation": [0, -3.0609707832336426, 0, 0], - "characterId": "0xf0835cb45669d7a2", - "actorModelId": 57, - "health": 215625.00000000032, - "placementTime": 1770784893453, - "parentObjectCharacterId": "0xa64c635491a20c17", - "itemDefinitionId": 1982, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x300000000013cc44", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0xf0835cb45669d7a2", - "containerDefinitionId": 23, - "items": { - "0x3000000000137adb": { - "itemDefinitionId": 48, - "slotId": 1, - "itemGuid": "0x3000000000137adb", - "containerGuid": "0x300000000013cc44", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 17, - "weapon": null - }, - "0x300000000010227f": { - "itemDefinitionId": 155, - "slotId": 2, - "itemGuid": "0x300000000010227f", - "containerGuid": "0x300000000013cc44", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 46, - "weapon": null - }, - "0x300000000010460d": { - "itemDefinitionId": 148, - "slotId": 3, - "itemGuid": "0x300000000010460d", - "containerGuid": "0x300000000013cc44", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000103d51": { - "itemDefinitionId": 1433, - "slotId": 4, - "itemGuid": "0x3000000000103d51", - "containerGuid": "0x300000000013cc44", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000007ec5b": { - "itemDefinitionId": 1433, - "slotId": 5, - "itemGuid": "0x300000000007ec5b", - "containerGuid": "0x300000000013cc44", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001371b3": { - "itemDefinitionId": 1433, - "slotId": 6, - "itemGuid": "0x30000000001371b3", - "containerGuid": "0x300000000013cc44", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000101060": { - "itemDefinitionId": 47, - "slotId": 7, - "itemGuid": "0x3000000000101060", - "containerGuid": "0x300000000013cc44", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 15, - "weapon": null - }, - "0x30000000000febaf": { - "itemDefinitionId": 111, - "slotId": 8, - "itemGuid": "0x30000000000febaf", - "containerGuid": "0x300000000013cc44", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000103d4e": { - "itemDefinitionId": 58, - "slotId": 9, - "itemGuid": "0x3000000000103d4e", - "containerGuid": "0x300000000013cc44", - "currentDurability": 4000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000103d4e", - "itemDefinitionId": 58, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000001384ff": { - "itemDefinitionId": 1895, - "slotId": 10, - "itemGuid": "0x30000000001384ff", - "containerGuid": "0x300000000013cc44", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000013a89d": { - "itemDefinitionId": 1467, - "slotId": 11, - "itemGuid": "0x300000000013a89d", - "containerGuid": "0x300000000013cc44", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 2, - "weapon": null - }, - "0x30000000001438c5": { - "itemDefinitionId": 58, - "slotId": 12, - "itemGuid": "0x30000000001438c5", - "containerGuid": "0x300000000013cc44", - "currentDurability": 4000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000001438c5", - "itemDefinitionId": 58, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000147a20": { - "itemDefinitionId": 16, - "slotId": 13, - "itemGuid": "0x3000000000147a20", - "containerGuid": "0x300000000013cc44", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 51, - "weapon": null - }, - "0x30000000001413a7": { - "itemDefinitionId": 3, - "slotId": 14, - "itemGuid": "0x30000000001413a7", - "containerGuid": "0x300000000013cc44", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000001413a7", - "itemDefinitionId": 3, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000014937a": { - "itemDefinitionId": 150, - "slotId": 15, - "itemGuid": "0x300000000014937a", - "containerGuid": "0x300000000013cc44", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001698af": { - "itemDefinitionId": 114, - "slotId": 16, - "itemGuid": "0x30000000001698af", - "containerGuid": "0x300000000013cc44", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 2, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "" - }, - "0x87223a6810bf7cae": { - "position": [ - -880.5744018554688, 60.39262771606445, - -1709.7271728515625, 1 - ], - "rotation": [0, -0.7666272521018982, 0, 0], - "characterId": "0x87223a6810bf7cae", - "actorModelId": 9205, - "health": 215625.00000000032, - "placementTime": 1770785653950, - "parentObjectCharacterId": "0xa64c635491a20c17", - "itemDefinitionId": 1447, - "slot": "", - "container": { - "itemDefinitionId": 1477, - "slotId": 31, - "itemGuid": "0x300000000013de3f", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x87223a6810bf7cae", - "containerDefinitionId": 48, - "items": { - "0x300000000013b2ae": { - "itemDefinitionId": 1368, - "slotId": 1, - "itemGuid": "0x300000000013b2ae", - "containerGuid": "0x300000000013de3f", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000146f57": { - "itemDefinitionId": 1535, - "slotId": 2, - "itemGuid": "0x3000000000146f57", - "containerGuid": "0x300000000013de3f", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 11, - "weapon": null - }, - "0x3000000000139920": { - "itemDefinitionId": 1436, - "slotId": 3, - "itemGuid": "0x3000000000139920", - "containerGuid": "0x300000000013de3f", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000142934": { - "itemDefinitionId": 72, - "slotId": 4, - "itemGuid": "0x3000000000142934", - "containerGuid": "0x300000000013de3f", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 2, - "weapon": null - }, - "0x30000000001698b6": { - "itemDefinitionId": 1353, - "slotId": 5, - "itemGuid": "0x30000000001698b6", - "containerGuid": "0x300000000013de3f", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 2, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "SmeltingEntity" - } - } - }, - "2": { - "position": [ - -886.685546875, 60.369407653808594, -1710.9912109375, 1 - ], - "rotation": [0, -1.5135562419891357, 0, 0], - "characterId": "0x46f709bd3afeedc5", - "actorModelId": 9411, - "health": 644920.5108350676, - "placementTime": 1770711911458, - "parentObjectCharacterId": "0x66e2813710249221", - "itemDefinitionId": 1897, - "slot": "Roof02", - "eulerAngle": -1.5135562419891357, - "occupiedWallSlots": { - "1": { - "position": [ - -888.0126953125, 60.392608642578125, -1713.3218994140625, - 1 - ], - "rotation": [-1.5135562419891357, 0, 0, 0], - "characterId": "0x5045d644c6074d84", - "actorModelId": 9181, - "health": 431250.00000000064, - "placementTime": 1770712417941, - "parentObjectCharacterId": "0x46f709bd3afeedc5", - "itemDefinitionId": 1881, - "slot": "LoveShackDoor", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 1013440036, - "grantedAccess": ["0x870613ce5e50d8e9"] - } - }, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": { - "0x22cbb69d69948420": { - "position": [ - -886.5211791992188, 62.8513298034668, -1711.212158203125, - 1 - ], - "rotation": [0, -0.4130539298057556, 0, 0], - "characterId": "0x22cbb69d69948420", - "actorModelId": 55, - "health": 86249.99999999984, - "placementTime": 1770715867442, - "parentObjectCharacterId": "0x46f709bd3afeedc5", - "itemDefinitionId": 97, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x3000000000109f76", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x22cbb69d69948420", - "containerDefinitionId": 23, - "items": {}, - "canAcceptItems": true, - "acceptedItems": [1353], - "isMutable": true - }, - "subEntityType": "CollectingEntity" - } - } - } - }, - "freeplaceEntities": {} - }, - "3": { - "position": [ - -891.7070922851562, 57.828407287597656, -1710.703369140625, 1 - ], - "rotation": [0, -1.5135561227798462, 0, 0], - "characterId": "0x4e01f34b0f103fb2", - "actorModelId": 51, - "health": 862500.0000000013, - "placementTime": 1770708827700, - "parentObjectCharacterId": "0x0a12d532f790e8f8", - "itemDefinitionId": 150, - "slot": "Structure03", - "eulerAngle": -1.5135561227798462, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - } - }, - "freeplaceEntities": {}, - "permissions": { - "0x870613ce5e50d8e9": { - "characterId": "0x870613ce5e50d8e9", - "characterName": "Bik", - "useContainers": true, - "build": true, - "demolish": true, - "visit": true - } - }, - "ownerCharacterId": "0x870613ce5e50d8e9", - "occupiedExpansionSlots": {}, - "occupiedRampSlots": {} - } - }, - "occupiedRampSlots": {}, - "occupiedShelterSlots": { - "2": { - "position": [ - -887.572998046875, 57.82850646972656, -1725.9642333984375, 1 - ], - "rotation": [0, -3.0843558311462402, 0, 0], - "characterId": "0x7ba5cd86b87d68f6", - "actorModelId": 53, - "health": 277142.59921595716, - "placementTime": 1770715320679, - "parentObjectCharacterId": "0x1d5e09ee82725e4f", - "itemDefinitionId": 154, - "slot": "Structure02", - "eulerAngle": -3.0843558311462402, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - } - }, - "occupiedUpperWallSlots": {}, - "occupiedWallSlots": { - "2": { - "position": [ - -890.2119750976562, 57.82850646972656, -1728.317138671875, 1 - ], - "rotation": [-1.5135596990585327, 0, 0, 0], - "characterId": "0x3189703b0f5c9e48", - "actorModelId": 49, - "health": 216500.00000000084, - "placementTime": 1770705255474, - "parentObjectCharacterId": "0x1d5e09ee82725e4f", - "itemDefinitionId": 148, - "slot": "PerimeterWall02", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 50795800, - "grantedAccess": ["0x870613ce5e50d8e9"] - }, - "3": { - "position": [ - -885.2201538085938, 57.82850646972656, -1728.6031494140625, 1 - ], - "rotation": [-1.5135596990585327, 0, 0, 0], - "characterId": "0x24cb441c7c05bf9a", - "actorModelId": 49, - "health": 454500.0000000005, - "placementTime": 1770705258430, - "parentObjectCharacterId": "0x1d5e09ee82725e4f", - "itemDefinitionId": 148, - "slot": "PerimeterWall03", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 1780888784, - "grantedAccess": ["0x870613ce5e50d8e9"] - }, - "4": { - "position": [ - -880.2283325195312, 57.82850646972656, -1728.88916015625, 1 - ], - "rotation": [-3.084359645843506, 0, 0, 0], - "characterId": "0xd622b93b1279e98a", - "actorModelId": 49, - "health": 697680.5437918587, - "placementTime": 1770705263665, - "parentObjectCharacterId": "0x1d5e09ee82725e4f", - "itemDefinitionId": 148, - "slot": "PerimeterWall04", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 50795800, - "grantedAccess": ["0x870613ce5e50d8e9"] - }, - "6": { - "position": [-879.65625, 57.82850646972656, -1718.9056396484375, 1], - "rotation": [-3.084359645843506, 0, 0, 0], - "characterId": "0xecca5f4b7b2e4db4", - "actorModelId": 49, - "health": 113113.63357844357, - "placementTime": 1770705264235, - "parentObjectCharacterId": "0x1d5e09ee82725e4f", - "itemDefinitionId": 148, - "slot": "PerimeterWall06", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 2809765886, - "grantedAccess": ["0x870613ce5e50d8e9"] - }, - "7": { - "position": [ - -879.3702392578125, 57.82850646972656, -1713.913818359375, 1 - ], - "rotation": [1.6280403137207031, 0, 0, 0], - "characterId": "0x33a3688e6ae70092", - "actorModelId": 49, - "health": 762872.5096713959, - "placementTime": 1770708636704, - "parentObjectCharacterId": "0x1d5e09ee82725e4f", - "itemDefinitionId": 148, - "slot": "PerimeterWall07", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 1458953084, - "grantedAccess": ["0x870613ce5e50d8e9"] - }, - "8": { - "position": [ - -884.362060546875, 57.82850646972656, -1713.6278076171875, 1 - ], - "rotation": [1.6280403137207031, 0, 0, 0], - "characterId": "0x36307e2d1382e774", - "actorModelId": 49, - "health": 797034.6364756343, - "placementTime": 1770705266084, - "parentObjectCharacterId": "0x1d5e09ee82725e4f", - "itemDefinitionId": 148, - "slot": "PerimeterWall08", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 50795800, - "grantedAccess": ["0x870613ce5e50d8e9"] - }, - "9": { - "position": [ - -889.3538818359375, 57.82850646972656, -1713.3416748046875, 1 - ], - "rotation": [1.6280403137207031, 0, 0, 0], - "characterId": "0x1a6d2eb78d45948b", - "actorModelId": 49, - "health": 862500.0000000013, - "placementTime": 1770708631850, - "parentObjectCharacterId": "0x1d5e09ee82725e4f", - "itemDefinitionId": 148, - "slot": "PerimeterWall09", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 50795800, - "grantedAccess": ["0x870613ce5e50d8e9"] - }, - "10": { - "position": [-894.345703125, 57.82850646972656, -1713.0556640625, 1], - "rotation": [0.05724029988050461, 0, 0, 0], - "characterId": "0x2924b897c08b5731", - "actorModelId": 49, - "health": 525013.7968305737, - "placementTime": 1770705458080, - "parentObjectCharacterId": "0x1d5e09ee82725e4f", - "itemDefinitionId": 148, - "slot": "PerimeterWall10", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 2809765886, - "grantedAccess": ["0x870613ce5e50d8e9"] - }, - "12": { - "position": [ - -894.9177856445312, 57.82850646972656, -1723.039306640625, 1 - ], - "rotation": [0.05724029988050461, 0, 0, 0], - "characterId": "0x350a33d3e22944be", - "actorModelId": 49, - "health": 386500.00000000047, - "placementTime": 1770705458972, - "parentObjectCharacterId": "0x1d5e09ee82725e4f", - "itemDefinitionId": 148, - "slot": "PerimeterWall12", - "ownerCharacterId": "0x870613ce5e50d8e9", - "passwordHash": 1013440036, - "grantedAccess": ["0x870613ce5e50d8e9"] - } - }, - "ownerCharacterId": "0x870613ce5e50d8e9", - "parentObjectCharacterId": "0x0000000000000000", - "permissions": { - "0x870613ce5e50d8e9": { - "characterId": "0x870613ce5e50d8e9", - "characterName": "Bik", - "useContainers": true, - "build": true, - "demolish": true, - "visit": true - } - }, - "placementTime": 1770704000452, - "position": [-887.2968139648438, 55.694305419921875, -1721.130859375, 1], - "rotation": [0, -1.5135596990585327, 0, 0], - "slot": "" - }, - { - "_id": { - "$oid": "698ca8f0194d07e49d45b510" - }, - "characterId": "0xd49596eabb878830", - "actorModelId": 9180, - "eulerAngle": -2.5622682571411133, - "freeplaceEntities": { - "0xc294270723391b1d": { - "position": [ - 2474.625244140625, 91.4568862915039, -467.5174255371094, 1 - ], - "rotation": [0, -1.0182852745056152, 0, 0], - "characterId": "0xc294270723391b1d", - "actorModelId": 57, - "health": 192708.33333333387, - "placementTime": 1770825389997, - "parentObjectCharacterId": "0xd49596eabb878830", - "itemDefinitionId": 1982, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x3000000000162f98", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0xc294270723391b1d", - "containerDefinitionId": 23, - "items": { - "0x3000000000161795": { - "itemDefinitionId": 16, - "slotId": 1, - "itemGuid": "0x3000000000161795", - "containerGuid": "0x3000000000162f98", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 40, - "weapon": null - }, - "0x3000000000162726": { - "itemDefinitionId": 48, - "slotId": 2, - "itemGuid": "0x3000000000162726", - "containerGuid": "0x3000000000162f98", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 5, - "weapon": null - }, - "0x3000000000150414": { - "itemDefinitionId": 1538, - "slotId": 3, - "itemGuid": "0x3000000000150414", - "containerGuid": "0x3000000000162f98", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000150414", - "itemDefinitionId": 1538, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000014d999": { - "itemDefinitionId": 155, - "slotId": 4, - "itemGuid": "0x300000000014d999", - "containerGuid": "0x3000000000162f98", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000162f4d": { - "itemDefinitionId": 114, - "slotId": 5, - "itemGuid": "0x3000000000162f4d", - "containerGuid": "0x3000000000162f98", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 3, - "weapon": null - }, - "0x300000000014d99a": { - "itemDefinitionId": 46, - "slotId": 6, - "itemGuid": "0x300000000014d99a", - "containerGuid": "0x3000000000162f98", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 4, - "weapon": null - }, - "0x30000000001a1664": { - "itemDefinitionId": 1373, - "slotId": 7, - "itemGuid": "0x30000000001a1664", - "containerGuid": "0x3000000000162f98", - "currentDurability": 1171, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000001a1664", - "itemDefinitionId": 1373, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000001a0b88": { - "itemDefinitionId": 1373, - "slotId": 8, - "itemGuid": "0x30000000001a0b88", - "containerGuid": "0x3000000000162f98", - "currentDurability": 1589, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000001a0b88", - "itemDefinitionId": 1373, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000001a1ce2": { - "itemDefinitionId": 1429, - "slotId": 9, - "itemGuid": "0x30000000001a1ce2", - "containerGuid": "0x3000000000162f98", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 3, - "weapon": null - }, - "0x30000000001a0be5": { - "itemDefinitionId": 1428, - "slotId": 10, - "itemGuid": "0x30000000001a0be5", - "containerGuid": "0x3000000000162f98", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 28, - "weapon": null - }, - "0x30000000001a0bcc": { - "itemDefinitionId": 1992, - "slotId": 11, - "itemGuid": "0x30000000001a0bcc", - "containerGuid": "0x3000000000162f98", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 23, - "weapon": null - }, - "0x30000000001a0bbd": { - "itemDefinitionId": 1719, - "slotId": 12, - "itemGuid": "0x30000000001a0bbd", - "containerGuid": "0x3000000000162f98", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 9, - "weapon": null - }, - "0x30000000001a0bea": { - "itemDefinitionId": 2325, - "slotId": 13, - "itemGuid": "0x30000000001a0bea", - "containerGuid": "0x3000000000162f98", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 7, - "weapon": null - }, - "0x30000000001a0bb5": { - "itemDefinitionId": 1998, - "slotId": 14, - "itemGuid": "0x30000000001a0bb5", - "containerGuid": "0x3000000000162f98", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 11, - "weapon": null - }, - "0x30000000001a1cd1": { - "itemDefinitionId": 78, - "slotId": 15, - "itemGuid": "0x30000000001a1cd1", - "containerGuid": "0x3000000000162f98", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 3, - "weapon": null - }, - "0x30000000001524de": { - "itemDefinitionId": 1538, - "slotId": 16, - "itemGuid": "0x30000000001524de", - "containerGuid": "0x3000000000162f98", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000001524de", - "itemDefinitionId": 1538, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000001503c8": { - "itemDefinitionId": 1538, - "slotId": 17, - "itemGuid": "0x30000000001503c8", - "containerGuid": "0x3000000000162f98", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000001503c8", - "itemDefinitionId": 1538, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000001503cb": { - "itemDefinitionId": 1538, - "slotId": 18, - "itemGuid": "0x30000000001503cb", - "containerGuid": "0x3000000000162f98", - "currentDurability": 960, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000001503cb", - "itemDefinitionId": 1538, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000001a280d": { - "itemDefinitionId": 58, - "slotId": 19, - "itemGuid": "0x30000000001a280d", - "containerGuid": "0x3000000000162f98", - "currentDurability": 3028, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000001a280d", - "itemDefinitionId": 58, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000001a1670": { - "itemDefinitionId": 1536, - "slotId": 20, - "itemGuid": "0x30000000001a1670", - "containerGuid": "0x3000000000162f98", - "currentDurability": 887, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000001a1670", - "itemDefinitionId": 1536, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "" - }, - "0x6ebd30a1e5b7e77f": { - "position": [ - 2475.85791015625, 91.45689392089844, -466.27362060546875, 1 - ], - "rotation": [0, 2.2130520343780518, 0, 0], - "characterId": "0x6ebd30a1e5b7e77f", - "actorModelId": 10065, - "health": 385416.66666666773, - "placementTime": 1770825402334, - "parentObjectCharacterId": "0xd49596eabb878830", - "itemDefinitionId": 3778, - "slot": "", - "eulerAngle": 2.2130520343780518, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - } - }, - "health": 770833.3333333355, - "itemDefinitionId": 1433, - "occupiedExpansionSlots": {}, - "occupiedRampSlots": {}, - "occupiedShelterSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedWallSlots": { - "1": { - "position": [ - 2478.875244140625, 91.4570541381836, -469.39569091796875, 1 - ], - "rotation": [-2.5622682571411133, 0, 0, 0], - "characterId": "0x6e6c2cabbc11e26e", - "actorModelId": 9181, - "health": 385416.66666666773, - "placementTime": 1770825381301, - "parentObjectCharacterId": "0xd49596eabb878830", - "itemDefinitionId": 1881, - "slot": "LoveShackDoor", - "ownerCharacterId": "0x2c0f6971a76a9b46", - "passwordHash": 1728811362, - "grantedAccess": ["0x2c0f6971a76a9b46"] - } - }, - "ownerCharacterId": "0x2c0f6971a76a9b46", - "parentObjectCharacterId": "0x0000000000000000", - "permissions": { - "0x2c0f6971a76a9b46": { - "characterId": "0x2c0f6971a76a9b46", - "characterName": "Ulio", - "useContainers": true, - "build": true, - "demolish": true, - "visit": true - } - }, - "placementTime": 1770825375849, - "position": [2477.4921875, 90.57785034179688, -467.1162414550781, 1], - "rotation": [0, -2.5622682571411133, 0, 0], - "slot": "" - }, - { - "_id": { - "$oid": "69902fb5194d07e49da622e4" - }, - "characterId": "0x65eb297ab7a59a46", - "actorModelId": 9180, - "eulerAngle": -1.389447808265686, - "freeplaceEntities": { - "0x1d76bba91e99df1f": { - "position": [1797.1104736328125, 126.122314453125, 3689.7255859375, 1], - "rotation": [0, -2.9604642391204834, 0, 0], - "characterId": "0x1d76bba91e99df1f", - "actorModelId": 55, - "health": 98749.99999999999, - "placementTime": 1771057279736, - "parentObjectCharacterId": "0x65eb297ab7a59a46", - "itemDefinitionId": 97, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x300000000021487f", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x1d76bba91e99df1f", - "containerDefinitionId": 23, - "items": { - "0x30000000002e461d": { - "itemDefinitionId": 1353, - "slotId": 1, - "itemGuid": "0x30000000002e461d", - "containerGuid": "0x300000000021487f", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 21, - "weapon": null - }, - "0x30000000002ef75c": { - "itemDefinitionId": 1535, - "slotId": 1, - "itemGuid": "0x30000000002ef75c", - "containerGuid": "0x300000000021487f", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 17, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [1353], - "isMutable": true - }, - "subEntityType": "CollectingEntity" - }, - "0xd926c23a9898fee5": { - "position": [1795.189453125, 126.12230682373047, 3691.486328125, 1], - "rotation": [0, 0.18290697038173676, 0, 0], - "characterId": "0xd926c23a9898fee5", - "actorModelId": 9406, - "health": 493750.00000000006, - "placementTime": 1771057578636, - "parentObjectCharacterId": "0x65eb297ab7a59a46", - "itemDefinitionId": 1891, - "slot": "", - "eulerAngle": 0.18290697038173676, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - }, - "0x5b03dfba941adf01": { - "position": [1798.34326171875, 126.12230682373047, 3689.544921875, 1], - "rotation": [0, -1.3860996961593628, 0, 0], - "characterId": "0x5b03dfba941adf01", - "actorModelId": 10065, - "health": 493750.00000000006, - "placementTime": 1771057818153, - "parentObjectCharacterId": "0x65eb297ab7a59a46", - "itemDefinitionId": 3778, - "slot": "", - "eulerAngle": -1.3860996961593628, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - }, - "0xb934715df3c3084d": { - "position": [1798.279296875, 126.421630859375, 3688.865234375, 1], - "rotation": [0, 0.1839713156223297, 0, 0], - "characterId": "0xb934715df3c3084d", - "actorModelId": 9428, - "health": 246875.00000000003, - "placementTime": 1771057874642, - "parentObjectCharacterId": "0x65eb297ab7a59a46", - "itemDefinitionId": 2034, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x30000000002148a1", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0xb934715df3c3084d", - "containerDefinitionId": 23, - "items": { - "0x3000000000225169": { - "itemDefinitionId": 2193, - "slotId": 1, - "itemGuid": "0x3000000000225169", - "containerGuid": "0x30000000002148a1", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000022516a": { - "itemDefinitionId": 2192, - "slotId": 2, - "itemGuid": "0x300000000022516a", - "containerGuid": "0x30000000002148a1", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [2203, 1353], - "isMutable": true - }, - "subEntityType": "CollectingEntity" - }, - "0xcb0772c8278c2677": { - "position": [ - 1798.480712890625, 126.42161560058594, 3689.941650390625, 1 - ], - "rotation": [0, -2.957716941833496, 0, 0], - "characterId": "0xcb0772c8278c2677", - "actorModelId": 57, - "health": 246875.00000000003, - "placementTime": 1771057896452, - "parentObjectCharacterId": "0x65eb297ab7a59a46", - "itemDefinitionId": 1982, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x30000000002148a2", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0xcb0772c8278c2677", - "containerDefinitionId": 23, - "items": { - "0x30000000001dc55b": { - "itemDefinitionId": 1719, - "slotId": 1, - "itemGuid": "0x30000000001dc55b", - "containerGuid": "0x30000000002148a2", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 28, - "weapon": null - }, - "0x3000000000210078": { - "itemDefinitionId": 1429, - "slotId": 2, - "itemGuid": "0x3000000000210078", - "containerGuid": "0x30000000002148a2", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 15, - "weapon": null - }, - "0x3000000000210082": { - "itemDefinitionId": 1511, - "slotId": 3, - "itemGuid": "0x3000000000210082", - "containerGuid": "0x30000000002148a2", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 16, - "weapon": null - }, - "0x30000000002115b8": { - "itemDefinitionId": 2325, - "slotId": 4, - "itemGuid": "0x30000000002115b8", - "containerGuid": "0x30000000002148a2", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 42, - "weapon": null - }, - "0x300000000021097f": { - "itemDefinitionId": 1998, - "slotId": 5, - "itemGuid": "0x300000000021097f", - "containerGuid": "0x30000000002148a2", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 17, - "weapon": null - }, - "0x300000000020cc0a": { - "itemDefinitionId": 1469, - "slotId": 6, - "itemGuid": "0x300000000020cc0a", - "containerGuid": "0x30000000002148a2", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 17, - "weapon": null - }, - "0x30000000001d4aff": { - "itemDefinitionId": 1992, - "slotId": 7, - "itemGuid": "0x30000000001d4aff", - "containerGuid": "0x30000000002148a2", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 53, - "weapon": null - }, - "0x300000000020cc06": { - "itemDefinitionId": 1428, - "slotId": 8, - "itemGuid": "0x300000000020cc06", - "containerGuid": "0x30000000002148a2", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 32, - "weapon": null - }, - "0x300000000020cc00": { - "itemDefinitionId": 1467, - "slotId": 9, - "itemGuid": "0x300000000020cc00", - "containerGuid": "0x30000000002148a2", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 6, - "weapon": null - }, - "0x30000000002100f7": { - "itemDefinitionId": 1386, - "slotId": 10, - "itemGuid": "0x30000000002100f7", - "containerGuid": "0x30000000002148a2", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 10, - "weapon": null - }, - "0x300000000020f7cd": { - "itemDefinitionId": 14, - "slotId": 11, - "itemGuid": "0x300000000020f7cd", - "containerGuid": "0x30000000002148a2", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000020f7cd", - "itemDefinitionId": 14, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000001dadc1": { - "itemDefinitionId": 1895, - "slotId": 12, - "itemGuid": "0x30000000001dadc1", - "containerGuid": "0x30000000002148a2", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "" - }, - "0xbf127e39c5f3752f": { - "position": [1798.36865234375, 126.42161560058594, 3689.54443359375, 1], - "rotation": [0, -2.9595327377319336, 0, 0], - "characterId": "0xbf127e39c5f3752f", - "actorModelId": 57, - "health": 246875.00000000003, - "placementTime": 1771057933525, - "parentObjectCharacterId": "0x65eb297ab7a59a46", - "itemDefinitionId": 1982, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x30000000002148a3", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0xbf127e39c5f3752f", - "containerDefinitionId": 23, - "items": { - "0x3000000000213682": { - "itemDefinitionId": 1373, - "slotId": 1, - "itemGuid": "0x3000000000213682", - "containerGuid": "0x30000000002148a3", - "currentDurability": 1720, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000213682", - "itemDefinitionId": 1373, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000211247": { - "itemDefinitionId": 10, - "slotId": 2, - "itemGuid": "0x3000000000211247", - "containerGuid": "0x30000000002148a3", - "currentDurability": 1301, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000211247", - "itemDefinitionId": 10, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000210985": { - "itemDefinitionId": 1991, - "slotId": 3, - "itemGuid": "0x3000000000210985", - "containerGuid": "0x30000000002148a3", - "currentDurability": 410, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000210985", - "itemDefinitionId": 1991, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000021007a": { - "itemDefinitionId": 1374, - "slotId": 4, - "itemGuid": "0x300000000021007a", - "containerGuid": "0x30000000002148a3", - "currentDurability": 580, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000021007a", - "itemDefinitionId": 1374, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000020cc0e": { - "itemDefinitionId": 2, - "slotId": 5, - "itemGuid": "0x300000000020cc0e", - "containerGuid": "0x30000000002148a3", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000020cc0e", - "itemDefinitionId": 2, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000020ef47": { - "itemDefinitionId": 3445, - "slotId": 6, - "itemGuid": "0x300000000020ef47", - "containerGuid": "0x30000000002148a3", - "currentDurability": 1000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000020ef47", - "itemDefinitionId": 3445, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000242c3f": { - "itemDefinitionId": 1373, - "slotId": 7, - "itemGuid": "0x3000000000242c3f", - "containerGuid": "0x30000000002148a3", - "currentDurability": 588, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000242c3f", - "itemDefinitionId": 1373, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000024233e": { - "itemDefinitionId": 1991, - "slotId": 8, - "itemGuid": "0x300000000024233e", - "containerGuid": "0x30000000002148a3", - "currentDurability": 1141, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000024233e", - "itemDefinitionId": 1991, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000002e6d24": { - "itemDefinitionId": 1373, - "slotId": 9, - "itemGuid": "0x30000000002e6d24", - "containerGuid": "0x30000000002148a3", - "currentDurability": 353, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002e6d24", - "itemDefinitionId": 1373, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000002e4f93": { - "itemDefinitionId": 1373, - "slotId": 10, - "itemGuid": "0x30000000002e4f93", - "containerGuid": "0x30000000002148a3", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002e4f93", - "itemDefinitionId": 1373, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "" - }, - "0x53dbc4ce6bd8cda4": { - "position": [ - 1797.994873046875, 126.12230682373047, 3690.88623046875, 1 - ], - "rotation": [0, 2.77350115776062, 0, 0], - "characterId": "0x53dbc4ce6bd8cda4", - "actorModelId": 36, - "health": 246875.00000000003, - "placementTime": 1771058264143, - "parentObjectCharacterId": "0x65eb297ab7a59a46", - "itemDefinitionId": 64, - "slot": "", - "container": { - "itemDefinitionId": 1711, - "slotId": 31, - "itemGuid": "0x30000000002148ad", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x53dbc4ce6bd8cda4", - "containerDefinitionId": 17, - "items": { - "0x30000000001d4afd": { - "itemDefinitionId": 1436, - "slotId": 1, - "itemGuid": "0x30000000001d4afd", - "containerGuid": "0x30000000002148ad", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "SmeltingEntity" - }, - "0x3691e00de5f2209a": { - "position": [1797.9488525390625, 126.12229919433594, 3687.05859375, 1], - "rotation": [0, -2.9588403701782227, 0, 0], - "characterId": "0x3691e00de5f2209a", - "actorModelId": 9205, - "health": 246875.00000000003, - "placementTime": 1771058302383, - "parentObjectCharacterId": "0x65eb297ab7a59a46", - "itemDefinitionId": 1447, - "slot": "", - "container": { - "itemDefinitionId": 1477, - "slotId": 31, - "itemGuid": "0x30000000002148ae", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x3691e00de5f2209a", - "containerDefinitionId": 48, - "items": { - "0x30000000001d8d7e": { - "itemDefinitionId": 1436, - "slotId": 1, - "itemGuid": "0x30000000001d8d7e", - "containerGuid": "0x30000000002148ae", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "SmeltingEntity" - }, - "0xffd74ddb3f3e1d9d": { - "position": [1797.402587890625, 126.12229919433594, 3687.1103515625, 1], - "rotation": [0, -1.3821492195129395, 0, 0], - "characterId": "0xffd74ddb3f3e1d9d", - "actorModelId": 57, - "health": 246875.00000000003, - "placementTime": 1771058384703, - "parentObjectCharacterId": "0x65eb297ab7a59a46", - "itemDefinitionId": 1982, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x30000000002148bc", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0xffd74ddb3f3e1d9d", - "containerDefinitionId": 23, - "items": { - "0x300000000020eef7": { - "itemDefinitionId": 111, - "slotId": 1, - "itemGuid": "0x300000000020eef7", - "containerGuid": "0x30000000002148bc", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 22, - "weapon": null - }, - "0x3000000000210070": { - "itemDefinitionId": 1988, - "slotId": 2, - "itemGuid": "0x3000000000210070", - "containerGuid": "0x30000000002148bc", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 4, - "weapon": null - }, - "0x300000000020d7c1": { - "itemDefinitionId": 56, - "slotId": 3, - "itemGuid": "0x300000000020d7c1", - "containerGuid": "0x30000000002148bc", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 81, - "weapon": null - }, - "0x30000000001d4eb3": { - "itemDefinitionId": 1987, - "slotId": 4, - "itemGuid": "0x30000000001d4eb3", - "containerGuid": "0x30000000002148bc", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 7, - "weapon": null - }, - "0x3000000000215ad5": { - "itemDefinitionId": 55, - "slotId": 5, - "itemGuid": "0x3000000000215ad5", - "containerGuid": "0x30000000002148bc", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 14, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "" - }, - "0xe2368d50b659a137": { - "position": [ - 1798.0260009765625, 126.12230682373047, 3687.56396484375, 1 - ], - "rotation": [0, 0.18414264917373657, 0, 0], - "characterId": "0xe2368d50b659a137", - "actorModelId": 57, - "health": 246875.00000000003, - "placementTime": 1771058418600, - "parentObjectCharacterId": "0x65eb297ab7a59a46", - "itemDefinitionId": 1982, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x30000000002148bd", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0xe2368d50b659a137", - "containerDefinitionId": 23, - "items": { - "0x30000000002112ca": { - "itemDefinitionId": 105, - "slotId": 1, - "itemGuid": "0x30000000002112ca", - "containerGuid": "0x30000000002148bd", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 248, - "weapon": null - }, - "0x30000000002100f0": { - "itemDefinitionId": 7, - "slotId": 2, - "itemGuid": "0x30000000002100f0", - "containerGuid": "0x30000000002148bd", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 46, - "weapon": null - }, - "0x300000000020ef3f": { - "itemDefinitionId": 1460, - "slotId": 3, - "itemGuid": "0x300000000020ef3f", - "containerGuid": "0x30000000002148bd", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 3, - "weapon": null - }, - "0x300000000020ef41": { - "itemDefinitionId": 1457, - "slotId": 4, - "itemGuid": "0x300000000020ef41", - "containerGuid": "0x30000000002148bd", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 3, - "weapon": null - }, - "0x30000000002100f9": { - "itemDefinitionId": 1458, - "slotId": 5, - "itemGuid": "0x30000000002100f9", - "containerGuid": "0x30000000002148bd", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 3, - "weapon": null - }, - "0x30000000002107e6": { - "itemDefinitionId": 1459, - "slotId": 6, - "itemGuid": "0x30000000002107e6", - "containerGuid": "0x30000000002148bd", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 3, - "weapon": null - }, - "0x30000000002124a3": { - "itemDefinitionId": 1402, - "slotId": 7, - "itemGuid": "0x30000000002124a3", - "containerGuid": "0x30000000002148bd", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 2, - "weapon": null - }, - "0x30000000002348ae": { - "itemDefinitionId": 2203, - "slotId": 8, - "itemGuid": "0x30000000002348ae", - "containerGuid": "0x30000000002148bd", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 5, - "weapon": null - }, - "0x30000000002ef74f": { - "itemDefinitionId": 3214, - "slotId": 9, - "itemGuid": "0x30000000002ef74f", - "containerGuid": "0x30000000002148bd", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 11, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "" - }, - "0xf538c157ecb54abc": { - "position": [1798.091796875, 126.12230682373047, 3687.95556640625, 1], - "rotation": [0, 0.18270136415958405, 0, 0], - "characterId": "0xf538c157ecb54abc", - "actorModelId": 57, - "health": 246875.00000000003, - "placementTime": 1771058484780, - "parentObjectCharacterId": "0x65eb297ab7a59a46", - "itemDefinitionId": 1982, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x30000000002148c0", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0xf538c157ecb54abc", - "containerDefinitionId": 23, - "items": { - "0x300000000020dd33": { - "itemDefinitionId": 2214, - "slotId": 1, - "itemGuid": "0x300000000020dd33", - "containerGuid": "0x30000000002148c0", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 14, - "weapon": null - }, - "0x300000000020e21a": { - "itemDefinitionId": 24, - "slotId": 2, - "itemGuid": "0x300000000020e21a", - "containerGuid": "0x30000000002148c0", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 3, - "weapon": null - }, - "0x300000000020dd32": { - "itemDefinitionId": 1751, - "slotId": 3, - "itemGuid": "0x300000000020dd32", - "containerGuid": "0x30000000002148c0", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 30, - "weapon": null - }, - "0x300000000020dd34": { - "itemDefinitionId": 78, - "slotId": 4, - "itemGuid": "0x300000000020dd34", - "containerGuid": "0x30000000002148c0", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 14, - "weapon": null - }, - "0x3000000000245080": { - "itemDefinitionId": 7, - "slotId": 5, - "itemGuid": "0x3000000000245080", - "containerGuid": "0x30000000002148c0", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 9, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "" - }, - "0xd5833d202b84fe61": { - "position": [ - 1796.531005859375, 126.12229919433594, 3690.19970703125, 1 - ], - "rotation": [0, 0.18465986847877502, 0, 0], - "characterId": "0xd5833d202b84fe61", - "actorModelId": 9456, - "health": 9875.000000000002, - "placementTime": 1771058749484, - "parentObjectCharacterId": "0x65eb297ab7a59a46", - "itemDefinitionId": 51, - "slot": "", - "eulerAngle": 0.18465986847877502, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - }, - "0x0cff56be132fc07d": { - "position": [ - 1796.6898193359375, 126.12229919433594, 3691.3369140625, 1 - ], - "rotation": [0, -2.9568614959716797, 0, 0], - "characterId": "0x0cff56be132fc07d", - "actorModelId": 57, - "health": 246875.00000000003, - "placementTime": 1771058801366, - "parentObjectCharacterId": "0x65eb297ab7a59a46", - "itemDefinitionId": 1982, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x3000000000215aaf", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x0cff56be132fc07d", - "containerDefinitionId": 23, - "items": { - "0x300000000020dd39": { - "itemDefinitionId": 1441, - "slotId": 1, - "itemGuid": "0x300000000020dd39", - "containerGuid": "0x3000000000215aaf", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 2, - "weapon": null - }, - "0x300000000020ef45": { - "itemDefinitionId": 110, - "slotId": 2, - "itemGuid": "0x300000000020ef45", - "containerGuid": "0x3000000000215aaf", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000020dd35": { - "itemDefinitionId": 2645, - "slotId": 3, - "itemGuid": "0x300000000020dd35", - "containerGuid": "0x3000000000215aaf", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 8, - "weapon": null - }, - "0x300000000020dd36": { - "itemDefinitionId": 2646, - "slotId": 4, - "itemGuid": "0x300000000020dd36", - "containerGuid": "0x3000000000215aaf", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 7, - "weapon": null - }, - "0x300000000020dd37": { - "itemDefinitionId": 2647, - "slotId": 5, - "itemGuid": "0x300000000020dd37", - "containerGuid": "0x3000000000215aaf", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 5, - "weapon": null - }, - "0x300000000020dd38": { - "itemDefinitionId": 2648, - "slotId": 6, - "itemGuid": "0x300000000020dd38", - "containerGuid": "0x3000000000215aaf", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 7, - "weapon": null - }, - "0x300000000020ef49": { - "itemDefinitionId": 2660, - "slotId": 7, - "itemGuid": "0x300000000020ef49", - "containerGuid": "0x3000000000215aaf", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000021099b": { - "itemDefinitionId": 1542, - "slotId": 8, - "itemGuid": "0x300000000021099b", - "containerGuid": "0x3000000000215aaf", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000021099b", - "itemDefinitionId": 1542, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "" - }, - "0x7baeb3a9b4a3fa46": { - "position": [ - 1794.758544921875, 126.12230682373047, 3691.59619140625, 1 - ], - "rotation": [0, 1.7513610124588013, 0, 0], - "characterId": "0x7baeb3a9b4a3fa46", - "actorModelId": 57, - "health": 246875.00000000003, - "placementTime": 1771058969972, - "parentObjectCharacterId": "0x65eb297ab7a59a46", - "itemDefinitionId": 1982, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x3000000000215ab0", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x7baeb3a9b4a3fa46", - "containerDefinitionId": 23, - "items": { - "0x3000000000214896": { - "itemDefinitionId": 48, - "slotId": 1, - "itemGuid": "0x3000000000214896", - "containerGuid": "0x3000000000215ab0", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 126, - "weapon": null - }, - "0x3000000000215ac5": { - "itemDefinitionId": 16, - "slotId": 2, - "itemGuid": "0x3000000000215ac5", - "containerGuid": "0x3000000000215ab0", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 364, - "weapon": null - }, - "0x3000000000215aee": { - "itemDefinitionId": 46, - "slotId": 3, - "itemGuid": "0x3000000000215aee", - "containerGuid": "0x3000000000215ab0", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 125, - "weapon": null - }, - "0x30000000002148a9": { - "itemDefinitionId": 47, - "slotId": 4, - "itemGuid": "0x30000000002148a9", - "containerGuid": "0x3000000000215ab0", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 12, - "weapon": null - }, - "0x3000000000216cec": { - "itemDefinitionId": 111, - "slotId": 5, - "itemGuid": "0x3000000000216cec", - "containerGuid": "0x3000000000215ab0", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000242c7a": { - "itemDefinitionId": 141, - "slotId": 6, - "itemGuid": "0x3000000000242c7a", - "containerGuid": "0x3000000000215ab0", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 8, - "weapon": null - }, - "0x3000000000242c86": { - "itemDefinitionId": 135, - "slotId": 7, - "itemGuid": "0x3000000000242c86", - "containerGuid": "0x3000000000215ab0", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 48, - "weapon": null - }, - "0x30000000002423e1": { - "itemDefinitionId": 109, - "slotId": 8, - "itemGuid": "0x30000000002423e1", - "containerGuid": "0x3000000000215ab0", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 5, - "weapon": null - }, - "0x3000000000245083": { - "itemDefinitionId": 39, - "slotId": 9, - "itemGuid": "0x3000000000245083", - "containerGuid": "0x3000000000215ab0", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 96, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "" - }, - "0xa0731e8c0be26167": { - "position": [1795.1796875, 126.12230682373047, 3691.52294921875, 1], - "rotation": [0, 1.7519675493240356, 0, 0], - "characterId": "0xa0731e8c0be26167", - "actorModelId": 57, - "health": 246875.00000000003, - "placementTime": 1771059007237, - "parentObjectCharacterId": "0x65eb297ab7a59a46", - "itemDefinitionId": 1982, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x3000000000215ab5", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0xa0731e8c0be26167", - "containerDefinitionId": 23, - "items": { - "0x300000000020d7aa": { - "itemDefinitionId": 134, - "slotId": 1, - "itemGuid": "0x300000000020d7aa", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 29, - "weapon": null - }, - "0x300000000020dd3a": { - "itemDefinitionId": 23, - "slotId": 2, - "itemGuid": "0x300000000020dd3a", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 227, - "weapon": null - }, - "0x30000000002150fd": { - "itemDefinitionId": 155, - "slotId": 3, - "itemGuid": "0x30000000002150fd", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 39, - "weapon": null - }, - "0x30000000001d4830": { - "itemDefinitionId": 142, - "slotId": 4, - "itemGuid": "0x30000000001d4830", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 19, - "weapon": null - }, - "0x300000000020dd3d": { - "itemDefinitionId": 72, - "slotId": 5, - "itemGuid": "0x300000000020dd3d", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 10, - "weapon": null - }, - "0x30000000002100ec": { - "itemDefinitionId": 1371, - "slotId": 6, - "itemGuid": "0x30000000002100ec", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 141, - "weapon": null - }, - "0x30000000002148d7": { - "itemDefinitionId": 111, - "slotId": 7, - "itemGuid": "0x30000000002148d7", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 23, - "weapon": null - }, - "0x300000000020ef08": { - "itemDefinitionId": 1725, - "slotId": 8, - "itemGuid": "0x300000000020ef08", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000020ef08", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000020ef1c": { - "itemDefinitionId": 1725, - "slotId": 9, - "itemGuid": "0x300000000020ef1c", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000020ef1c", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000020ef25": { - "itemDefinitionId": 1725, - "slotId": 10, - "itemGuid": "0x300000000020ef25", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000020ef25", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000020dd29": { - "itemDefinitionId": 1725, - "slotId": 11, - "itemGuid": "0x300000000020dd29", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000020dd29", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000002124b0": { - "itemDefinitionId": 1725, - "slotId": 12, - "itemGuid": "0x30000000002124b0", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002124b0", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000020ef2a": { - "itemDefinitionId": 1725, - "slotId": 13, - "itemGuid": "0x300000000020ef2a", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000020ef2a", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000216cd1": { - "itemDefinitionId": 114, - "slotId": 14, - "itemGuid": "0x3000000000216cd1", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 6, - "weapon": null - }, - "0x3000000000242f90": { - "itemDefinitionId": 1725, - "slotId": 15, - "itemGuid": "0x3000000000242f90", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000242f90", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000243e56": { - "itemDefinitionId": 1725, - "slotId": 16, - "itemGuid": "0x3000000000243e56", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000243e56", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000243e5a": { - "itemDefinitionId": 1725, - "slotId": 17, - "itemGuid": "0x3000000000243e5a", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000243e5a", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000243e4c": { - "itemDefinitionId": 1725, - "slotId": 18, - "itemGuid": "0x3000000000243e4c", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000243e4c", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000243e54": { - "itemDefinitionId": 1725, - "slotId": 19, - "itemGuid": "0x3000000000243e54", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000243e54", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000243e5f": { - "itemDefinitionId": 1725, - "slotId": 20, - "itemGuid": "0x3000000000243e5f", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000243e5f", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000243e72": { - "itemDefinitionId": 1725, - "slotId": 21, - "itemGuid": "0x3000000000243e72", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000243e72", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000243e67": { - "itemDefinitionId": 1725, - "slotId": 22, - "itemGuid": "0x3000000000243e67", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000243e67", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000243e69": { - "itemDefinitionId": 1725, - "slotId": 23, - "itemGuid": "0x3000000000243e69", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000243e69", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000243e6c": { - "itemDefinitionId": 1725, - "slotId": 24, - "itemGuid": "0x3000000000243e6c", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000243e6c", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000243e6f": { - "itemDefinitionId": 1725, - "slotId": 25, - "itemGuid": "0x3000000000243e6f", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000243e6f", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000002ed8ca": { - "itemDefinitionId": 1725, - "slotId": 26, - "itemGuid": "0x30000000002ed8ca", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002ed8ca", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000002ed8d3": { - "itemDefinitionId": 1725, - "slotId": 27, - "itemGuid": "0x30000000002ed8d3", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002ed8d3", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000002ed8dc": { - "itemDefinitionId": 1725, - "slotId": 28, - "itemGuid": "0x30000000002ed8dc", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002ed8dc", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000002ed8f2": { - "itemDefinitionId": 1725, - "slotId": 29, - "itemGuid": "0x30000000002ed8f2", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002ed8f2", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000002ed8f8": { - "itemDefinitionId": 1725, - "slotId": 30, - "itemGuid": "0x30000000002ed8f8", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002ed8f8", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000002e4fe6": { - "itemDefinitionId": 1725, - "slotId": 31, - "itemGuid": "0x30000000002e4fe6", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002e4fe6", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000002e4fe9": { - "itemDefinitionId": 1725, - "slotId": 32, - "itemGuid": "0x30000000002e4fe9", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002e4fe9", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000002e4fed": { - "itemDefinitionId": 1725, - "slotId": 33, - "itemGuid": "0x30000000002e4fed", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002e4fed", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000002e4ff1": { - "itemDefinitionId": 1725, - "slotId": 34, - "itemGuid": "0x30000000002e4ff1", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002e4ff1", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000002e4fff": { - "itemDefinitionId": 1725, - "slotId": 35, - "itemGuid": "0x30000000002e4fff", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002e4fff", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000002e4fd8": { - "itemDefinitionId": 1725, - "slotId": 36, - "itemGuid": "0x30000000002e4fd8", - "containerGuid": "0x3000000000215ab5", - "currentDurability": 500, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002e4fd8", - "itemDefinitionId": 1725, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "" - }, - "0xa377c3b0397cff20": { - "position": [ - 1795.5980224609375, 126.12230682373047, 3691.45458984375, 1 - ], - "rotation": [0, 1.7519978284835815, 0, 0], - "characterId": "0xa377c3b0397cff20", - "actorModelId": 57, - "health": 246875.00000000003, - "placementTime": 1771059158592, - "parentObjectCharacterId": "0x65eb297ab7a59a46", - "itemDefinitionId": 1982, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x3000000000215abe", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0xa377c3b0397cff20", - "containerDefinitionId": 23, - "items": { - "0x30000000002124c0": { - "itemDefinitionId": 1378, - "slotId": 1, - "itemGuid": "0x30000000002124c0", - "containerGuid": "0x3000000000215abe", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 2, - "weapon": null - }, - "0x3000000000216cdc": { - "itemDefinitionId": 1982, - "slotId": 2, - "itemGuid": "0x3000000000216cdc", - "containerGuid": "0x3000000000215abe", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "" - }, - "0x6c82fe74c905fb3f": { - "position": [ - 1796.5050048828125, 126.12229919433594, 3688.725341796875, 1 - ], - "rotation": [0, 1.7517894506454468, 0, 0], - "characterId": "0x6c82fe74c905fb3f", - "actorModelId": 57, - "health": 246875.00000000003, - "placementTime": 1771059600793, - "parentObjectCharacterId": "0x65eb297ab7a59a46", - "itemDefinitionId": 1982, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x3000000000215ace", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x6c82fe74c905fb3f", - "containerDefinitionId": 23, - "items": { - "0x30000000001d8d83": { - "itemDefinitionId": 1538, - "slotId": 1, - "itemGuid": "0x30000000001d8d83", - "containerGuid": "0x3000000000215ace", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000001d8d83", - "itemDefinitionId": 1538, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000020ef4e": { - "itemDefinitionId": 73, - "slotId": 2, - "itemGuid": "0x300000000020ef4e", - "containerGuid": "0x3000000000215ace", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 6, - "weapon": null - }, - "0x30000000001da392": { - "itemDefinitionId": 1538, - "slotId": 3, - "itemGuid": "0x30000000001da392", - "containerGuid": "0x3000000000215ace", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000001da392", - "itemDefinitionId": 1538, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000001d6c7f": { - "itemDefinitionId": 1538, - "slotId": 4, - "itemGuid": "0x30000000001d6c7f", - "containerGuid": "0x3000000000215ace", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000001d6c7f", - "itemDefinitionId": 1538, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000001d6c95": { - "itemDefinitionId": 1538, - "slotId": 5, - "itemGuid": "0x30000000001d6c95", - "containerGuid": "0x3000000000215ace", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000001d6c95", - "itemDefinitionId": 1538, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000001d8d56": { - "itemDefinitionId": 1538, - "slotId": 6, - "itemGuid": "0x30000000001d8d56", - "containerGuid": "0x3000000000215ace", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000001d8d56", - "itemDefinitionId": 1538, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000001da38d": { - "itemDefinitionId": 1538, - "slotId": 7, - "itemGuid": "0x30000000001da38d", - "containerGuid": "0x3000000000215ace", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000001da38d", - "itemDefinitionId": 1538, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000002109b1": { - "itemDefinitionId": 1538, - "slotId": 8, - "itemGuid": "0x30000000002109b1", - "containerGuid": "0x3000000000215ace", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002109b1", - "itemDefinitionId": 1538, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000001cc463": { - "itemDefinitionId": 90, - "slotId": 9, - "itemGuid": "0x30000000001cc463", - "containerGuid": "0x3000000000215ace", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001dad86": { - "itemDefinitionId": 1731, - "slotId": 10, - "itemGuid": "0x30000000001dad86", - "containerGuid": "0x3000000000215ace", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001d6c26": { - "itemDefinitionId": 1538, - "slotId": 11, - "itemGuid": "0x30000000001d6c26", - "containerGuid": "0x3000000000215ace", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000001d6c26", - "itemDefinitionId": 1538, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000001d6c23": { - "itemDefinitionId": 1538, - "slotId": 12, - "itemGuid": "0x30000000001d6c23", - "containerGuid": "0x3000000000215ace", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000001d6c23", - "itemDefinitionId": 1538, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000216294": { - "itemDefinitionId": 1538, - "slotId": 13, - "itemGuid": "0x3000000000216294", - "containerGuid": "0x3000000000215ace", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000216294", - "itemDefinitionId": 1538, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000213ec1": { - "itemDefinitionId": 1538, - "slotId": 14, - "itemGuid": "0x3000000000213ec1", - "containerGuid": "0x3000000000215ace", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000213ec1", - "itemDefinitionId": 1538, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000002e42d2": { - "itemDefinitionId": 1538, - "slotId": 15, - "itemGuid": "0x30000000002e42d2", - "containerGuid": "0x3000000000215ace", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002e42d2", - "itemDefinitionId": 1538, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "" - }, - "0x1aecc7fde35bcd0d": { - "position": [ - 1794.054443359375, 126.12230682373047, 3687.516357421875, 1 - ], - "rotation": [0, 0.18487730622291565, 0, 0], - "characterId": "0x1aecc7fde35bcd0d", - "actorModelId": 57, - "health": 246875.00000000003, - "placementTime": 1771061017170, - "parentObjectCharacterId": "0x65eb297ab7a59a46", - "itemDefinitionId": 1982, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x3000000000216cd5", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x1aecc7fde35bcd0d", - "containerDefinitionId": 23, - "items": { - "0x30000000001d6c96": { - "itemDefinitionId": 82, - "slotId": 1, - "itemGuid": "0x30000000001d6c96", - "containerGuid": "0x3000000000216cd5", - "currentDurability": 1310, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000001d6c96", - "itemDefinitionId": 82, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000001db868": { - "itemDefinitionId": 82, - "slotId": 2, - "itemGuid": "0x30000000001db868", - "containerGuid": "0x3000000000216cd5", - "currentDurability": 1278, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000001db868", - "itemDefinitionId": 82, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000020f82e": { - "itemDefinitionId": 82, - "slotId": 3, - "itemGuid": "0x300000000020f82e", - "containerGuid": "0x3000000000216cd5", - "currentDurability": 1205, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000020f82e", - "itemDefinitionId": 82, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000020f857": { - "itemDefinitionId": 82, - "slotId": 4, - "itemGuid": "0x300000000020f857", - "containerGuid": "0x3000000000216cd5", - "currentDurability": 1888, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000020f857", - "itemDefinitionId": 82, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000020d472": { - "itemDefinitionId": 58, - "slotId": 5, - "itemGuid": "0x300000000020d472", - "containerGuid": "0x3000000000216cd5", - "currentDurability": 3034, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000020d472", - "itemDefinitionId": 58, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000021008a": { - "itemDefinitionId": 82, - "slotId": 6, - "itemGuid": "0x300000000021008a", - "containerGuid": "0x3000000000216cd5", - "currentDurability": 905, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000021008a", - "itemDefinitionId": 82, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x300000000020f82d": { - "itemDefinitionId": 58, - "slotId": 7, - "itemGuid": "0x300000000020f82d", - "containerGuid": "0x3000000000216cd5", - "currentDurability": 4000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x300000000020f82d", - "itemDefinitionId": 58, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000002100bd": { - "itemDefinitionId": 1536, - "slotId": 8, - "itemGuid": "0x30000000002100bd", - "containerGuid": "0x3000000000216cd5", - "currentDurability": 628, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002100bd", - "itemDefinitionId": 1536, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000001d8d54": { - "itemDefinitionId": 82, - "slotId": 9, - "itemGuid": "0x30000000001d8d54", - "containerGuid": "0x3000000000216cd5", - "currentDurability": 540, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000001d8d54", - "itemDefinitionId": 82, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000001d6c80": { - "itemDefinitionId": 82, - "slotId": 10, - "itemGuid": "0x30000000001d6c80", - "containerGuid": "0x3000000000216cd5", - "currentDurability": 1709, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000001d6c80", - "itemDefinitionId": 82, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000001d6c81": { - "itemDefinitionId": 82, - "slotId": 11, - "itemGuid": "0x30000000001d6c81", - "containerGuid": "0x3000000000216cd5", - "currentDurability": 748, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000001d6c81", - "itemDefinitionId": 82, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000210066": { - "itemDefinitionId": 3, - "slotId": 12, - "itemGuid": "0x3000000000210066", - "containerGuid": "0x3000000000216cd5", - "currentDurability": 1312, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000210066", - "itemDefinitionId": 3, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000001d6c9a": { - "itemDefinitionId": 82, - "slotId": 13, - "itemGuid": "0x30000000001d6c9a", - "containerGuid": "0x3000000000216cd5", - "currentDurability": 1810, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000001d6c9a", - "itemDefinitionId": 82, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000242c4b": { - "itemDefinitionId": 1536, - "slotId": 14, - "itemGuid": "0x3000000000242c4b", - "containerGuid": "0x3000000000216cd5", - "currentDurability": 1806, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000242c4b", - "itemDefinitionId": 1536, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000001d6c2b": { - "itemDefinitionId": 1903, - "slotId": 15, - "itemGuid": "0x30000000001d6c2b", - "containerGuid": "0x3000000000216cd5", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000001d6c2b", - "itemDefinitionId": 1903, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000216280": { - "itemDefinitionId": 82, - "slotId": 16, - "itemGuid": "0x3000000000216280", - "containerGuid": "0x3000000000216cd5", - "currentDurability": 559, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000216280", - "itemDefinitionId": 82, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000242c09": { - "itemDefinitionId": 82, - "slotId": 17, - "itemGuid": "0x3000000000242c09", - "containerGuid": "0x3000000000216cd5", - "currentDurability": 1094, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000242c09", - "itemDefinitionId": 82, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000002424fb": { - "itemDefinitionId": 3, - "slotId": 18, - "itemGuid": "0x30000000002424fb", - "containerGuid": "0x3000000000216cd5", - "currentDurability": 1872, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002424fb", - "itemDefinitionId": 3, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000001db856": { - "itemDefinitionId": 1903, - "slotId": 19, - "itemGuid": "0x30000000001db856", - "containerGuid": "0x3000000000216cd5", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000001db856", - "itemDefinitionId": 1903, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000002100be": { - "itemDefinitionId": 1536, - "slotId": 20, - "itemGuid": "0x30000000002100be", - "containerGuid": "0x3000000000216cd5", - "currentDurability": 576, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002100be", - "itemDefinitionId": 1536, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000002e6d31": { - "itemDefinitionId": 1536, - "slotId": 21, - "itemGuid": "0x30000000002e6d31", - "containerGuid": "0x3000000000216cd5", - "currentDurability": 1703, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002e6d31", - "itemDefinitionId": 1536, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000002e4345": { - "itemDefinitionId": 1903, - "slotId": 22, - "itemGuid": "0x30000000002e4345", - "containerGuid": "0x3000000000216cd5", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002e4345", - "itemDefinitionId": 1903, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000002e6577": { - "itemDefinitionId": 58, - "slotId": 23, - "itemGuid": "0x30000000002e6577", - "containerGuid": "0x3000000000216cd5", - "currentDurability": 4000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002e6577", - "itemDefinitionId": 58, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000002e4624": { - "itemDefinitionId": 3, - "slotId": 24, - "itemGuid": "0x30000000002e4624", - "containerGuid": "0x3000000000216cd5", - "currentDurability": 1856, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002e4624", - "itemDefinitionId": 3, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000002e642b": { - "itemDefinitionId": 82, - "slotId": 25, - "itemGuid": "0x30000000002e642b", - "containerGuid": "0x3000000000216cd5", - "currentDurability": 1204, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002e642b", - "itemDefinitionId": 82, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000002e4355": { - "itemDefinitionId": 82, - "slotId": 26, - "itemGuid": "0x30000000002e4355", - "containerGuid": "0x3000000000216cd5", - "currentDurability": 869, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002e4355", - "itemDefinitionId": 82, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x30000000002e6678": { - "itemDefinitionId": 3, - "slotId": 27, - "itemGuid": "0x30000000002e6678", - "containerGuid": "0x3000000000216cd5", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x30000000002e6678", - "itemDefinitionId": 3, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - }, - "0x3000000000210068": { - "itemDefinitionId": 3, - "slotId": 28, - "itemGuid": "0x3000000000210068", - "containerGuid": "0x3000000000216cd5", - "currentDurability": 1784, - "debugFlag": "unset", - "stackCount": 1, - "weapon": { - "itemGuid": "0x3000000000210068", - "itemDefinitionId": 3, - "ammoCount": 0, - "reloadTimer": null, - "currentReloadCount": 0 - } - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "" - }, - "0x6c062bbf5827c27b": { - "position": [1793.9710693359375, 126.122314453125, 3688.1435546875, 1], - "rotation": [0, 1.7593305110931396, 0, 0], - "characterId": "0x6c062bbf5827c27b", - "actorModelId": 57, - "health": 246875.00000000003, - "placementTime": 1771061042289, - "parentObjectCharacterId": "0x65eb297ab7a59a46", - "itemDefinitionId": 1982, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x3000000000216cd6", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x6c062bbf5827c27b", - "containerDefinitionId": 23, - "items": { - "0x300000000020e94e": { - "itemDefinitionId": 1696, - "slotId": 1, - "itemGuid": "0x300000000020e94e", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001d3cd6": { - "itemDefinitionId": 1696, - "slotId": 2, - "itemGuid": "0x30000000001d3cd6", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001cc461": { - "itemDefinitionId": 1701, - "slotId": 3, - "itemGuid": "0x30000000001cc461", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001fc401": { - "itemDefinitionId": 1701, - "slotId": 4, - "itemGuid": "0x30000000001fc401", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001dad6c": { - "itemDefinitionId": 1701, - "slotId": 5, - "itemGuid": "0x30000000001dad6c", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001d51fb": { - "itemDefinitionId": 3460, - "slotId": 6, - "itemGuid": "0x30000000001d51fb", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001dae9a": { - "itemDefinitionId": 3460, - "slotId": 7, - "itemGuid": "0x30000000001dae9a", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000017b033": { - "itemDefinitionId": 1696, - "slotId": 8, - "itemGuid": "0x300000000017b033", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000020d7ba": { - "itemDefinitionId": 1696, - "slotId": 9, - "itemGuid": "0x300000000020d7ba", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001dad84": { - "itemDefinitionId": 1701, - "slotId": 10, - "itemGuid": "0x30000000001dad84", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001c8ebb": { - "itemDefinitionId": 3460, - "slotId": 11, - "itemGuid": "0x30000000001c8ebb", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001cc462": { - "itemDefinitionId": 3460, - "slotId": 12, - "itemGuid": "0x30000000001cc462", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001e2e9d": { - "itemDefinitionId": 1701, - "slotId": 13, - "itemGuid": "0x30000000001e2e9d", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001cfb6b": { - "itemDefinitionId": 1701, - "slotId": 14, - "itemGuid": "0x30000000001cfb6b", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000020cc04": { - "itemDefinitionId": 1696, - "slotId": 15, - "itemGuid": "0x300000000020cc04", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001dad83": { - "itemDefinitionId": 1696, - "slotId": 16, - "itemGuid": "0x30000000001dad83", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001d3cd7": { - "itemDefinitionId": 1701, - "slotId": 17, - "itemGuid": "0x30000000001d3cd7", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001cfb5e": { - "itemDefinitionId": 90, - "slotId": 18, - "itemGuid": "0x30000000001cfb5e", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000017b034": { - "itemDefinitionId": 9, - "slotId": 19, - "itemGuid": "0x300000000017b034", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000020c66e": { - "itemDefinitionId": 1701, - "slotId": 20, - "itemGuid": "0x300000000020c66e", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001cc460": { - "itemDefinitionId": 1696, - "slotId": 21, - "itemGuid": "0x30000000001cc460", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000242b59": { - "itemDefinitionId": 1701, - "slotId": 22, - "itemGuid": "0x3000000000242b59", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000242b24": { - "itemDefinitionId": 1701, - "slotId": 23, - "itemGuid": "0x3000000000242b24", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000242154": { - "itemDefinitionId": 1701, - "slotId": 24, - "itemGuid": "0x3000000000242154", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000243cb2": { - "itemDefinitionId": 1696, - "slotId": 25, - "itemGuid": "0x3000000000243cb2", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000243ccb": { - "itemDefinitionId": 1696, - "slotId": 26, - "itemGuid": "0x3000000000243ccb", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000002422b6": { - "itemDefinitionId": 1696, - "slotId": 27, - "itemGuid": "0x30000000002422b6", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000243604": { - "itemDefinitionId": 1731, - "slotId": 28, - "itemGuid": "0x3000000000243604", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001cfb6c": { - "itemDefinitionId": 1730, - "slotId": 29, - "itemGuid": "0x30000000001cfb6c", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001e5242": { - "itemDefinitionId": 1701, - "slotId": 30, - "itemGuid": "0x30000000001e5242", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000002e9d36": { - "itemDefinitionId": 3460, - "slotId": 31, - "itemGuid": "0x30000000002e9d36", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000026f6e1": { - "itemDefinitionId": 1701, - "slotId": 32, - "itemGuid": "0x300000000026f6e1", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000274f2b": { - "itemDefinitionId": 1701, - "slotId": 33, - "itemGuid": "0x3000000000274f2b", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000002ea299": { - "itemDefinitionId": 3460, - "slotId": 34, - "itemGuid": "0x30000000002ea299", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000002ed687": { - "itemDefinitionId": 1696, - "slotId": 35, - "itemGuid": "0x30000000002ed687", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000002e3c79": { - "itemDefinitionId": 1696, - "slotId": 36, - "itemGuid": "0x30000000002e3c79", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000002dc166": { - "itemDefinitionId": 1701, - "slotId": 37, - "itemGuid": "0x30000000002dc166", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000026f6e0": { - "itemDefinitionId": 1696, - "slotId": 38, - "itemGuid": "0x300000000026f6e0", - "containerGuid": "0x3000000000216cd6", - "currentDurability": 0, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "" - }, - "0x8bf3e14196951e42": { - "position": [1794.1226806640625, 126.12229919433594, 3688.953125, 1], - "rotation": [0, 1.7493373155593872, 0, 0], - "characterId": "0x8bf3e14196951e42", - "actorModelId": 57, - "health": 246875.00000000003, - "placementTime": 1771061058627, - "parentObjectCharacterId": "0x65eb297ab7a59a46", - "itemDefinitionId": 1982, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x3000000000216cd7", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x8bf3e14196951e42", - "containerDefinitionId": 23, - "items": {}, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "" - }, - "0x06124ad9fc47d5ce": { - "position": [1794.265625, 126.122314453125, 3689.77734375, 1], - "rotation": [0, 1.7514333724975586, 0, 0], - "characterId": "0x06124ad9fc47d5ce", - "actorModelId": 57, - "health": 246875.00000000003, - "placementTime": 1771061141235, - "parentObjectCharacterId": "0x65eb297ab7a59a46", - "itemDefinitionId": 1982, - "slot": "", - "container": { - "itemDefinitionId": 1506, - "slotId": 31, - "itemGuid": "0x3000000000216ce1", - "containerGuid": "0xFFFFFFFFFFFFFFFF", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null, - "loadoutItemOwnerGuid": "0x06124ad9fc47d5ce", - "containerDefinitionId": 23, - "items": { - "0x300000000021098b": { - "itemDefinitionId": 2148, - "slotId": 1, - "itemGuid": "0x300000000021098b", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000020f7e1": { - "itemDefinitionId": 2271, - "slotId": 2, - "itemGuid": "0x300000000020f7e1", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 1000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001d52ce": { - "itemDefinitionId": 2255, - "slotId": 3, - "itemGuid": "0x30000000001d52ce", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000020dd58": { - "itemDefinitionId": 2217, - "slotId": 4, - "itemGuid": "0x300000000020dd58", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 5400, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000021098c": { - "itemDefinitionId": 2148, - "slotId": 5, - "itemGuid": "0x300000000021098c", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000020dd60": { - "itemDefinitionId": 2169, - "slotId": 6, - "itemGuid": "0x300000000020dd60", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 100, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001dea7b": { - "itemDefinitionId": 2271, - "slotId": 7, - "itemGuid": "0x30000000001dea7b", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 1000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001dc574": { - "itemDefinitionId": 2113, - "slotId": 8, - "itemGuid": "0x30000000001dc574", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001d807d": { - "itemDefinitionId": 2113, - "slotId": 9, - "itemGuid": "0x30000000001d807d", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000002103d4": { - "itemDefinitionId": 2217, - "slotId": 10, - "itemGuid": "0x30000000002103d4", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 5400, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001dc571": { - "itemDefinitionId": 2114, - "slotId": 11, - "itemGuid": "0x30000000001dc571", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000021010f": { - "itemDefinitionId": 2116, - "slotId": 12, - "itemGuid": "0x300000000021010f", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000020cbf8": { - "itemDefinitionId": 2169, - "slotId": 13, - "itemGuid": "0x300000000020cbf8", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 100, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001db1a1": { - "itemDefinitionId": 2271, - "slotId": 14, - "itemGuid": "0x30000000001db1a1", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 1000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000020dd63": { - "itemDefinitionId": 2114, - "slotId": 15, - "itemGuid": "0x300000000020dd63", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000020cbf2": { - "itemDefinitionId": 2217, - "slotId": 16, - "itemGuid": "0x300000000020cbf2", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 5400, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001dfc91": { - "itemDefinitionId": 2124, - "slotId": 17, - "itemGuid": "0x30000000001dfc91", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001dd815": { - "itemDefinitionId": 2124, - "slotId": 18, - "itemGuid": "0x30000000001dd815", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001d9b4a": { - "itemDefinitionId": 2124, - "slotId": 19, - "itemGuid": "0x30000000001d9b4a", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001d9b58": { - "itemDefinitionId": 2124, - "slotId": 20, - "itemGuid": "0x30000000001d9b58", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001db19e": { - "itemDefinitionId": 2124, - "slotId": 21, - "itemGuid": "0x30000000001db19e", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001d4b20": { - "itemDefinitionId": 2124, - "slotId": 22, - "itemGuid": "0x30000000001d4b20", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000243708": { - "itemDefinitionId": 2177, - "slotId": 23, - "itemGuid": "0x3000000000243708", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000002436fb": { - "itemDefinitionId": 2177, - "slotId": 24, - "itemGuid": "0x30000000002436fb", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000243707": { - "itemDefinitionId": 2088, - "slotId": 25, - "itemGuid": "0x3000000000243707", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000243704": { - "itemDefinitionId": 2088, - "slotId": 26, - "itemGuid": "0x3000000000243704", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000242c9b": { - "itemDefinitionId": 1529, - "slotId": 27, - "itemGuid": "0x3000000000242c9b", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001d5e27": { - "itemDefinitionId": 1803, - "slotId": 28, - "itemGuid": "0x30000000001d5e27", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001d86b9": { - "itemDefinitionId": 1803, - "slotId": 29, - "itemGuid": "0x30000000001d86b9", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001d5e1b": { - "itemDefinitionId": 1803, - "slotId": 30, - "itemGuid": "0x30000000001d5e1b", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001d5e23": { - "itemDefinitionId": 1803, - "slotId": 31, - "itemGuid": "0x30000000001d5e23", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x300000000021099c": { - "itemDefinitionId": 2124, - "slotId": 32, - "itemGuid": "0x300000000021099c", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x3000000000242161": { - "itemDefinitionId": 2114, - "slotId": 33, - "itemGuid": "0x3000000000242161", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000001d9797": { - "itemDefinitionId": 2124, - "slotId": 34, - "itemGuid": "0x30000000001d9797", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000002e34ee": { - "itemDefinitionId": 1803, - "slotId": 35, - "itemGuid": "0x30000000002e34ee", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000002e6671": { - "itemDefinitionId": 2216, - "slotId": 36, - "itemGuid": "0x30000000002e6671", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 5400, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000002e6676": { - "itemDefinitionId": 2217, - "slotId": 37, - "itemGuid": "0x30000000002e6676", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 5400, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000002e461c": { - "itemDefinitionId": 2216, - "slotId": 38, - "itemGuid": "0x30000000002e461c", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 5400, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - }, - "0x30000000002e60fd": { - "itemDefinitionId": 2117, - "slotId": 39, - "itemGuid": "0x30000000002e60fd", - "containerGuid": "0x3000000000216ce1", - "currentDurability": 2000, - "debugFlag": "unset", - "stackCount": 1, - "weapon": null - } - }, - "canAcceptItems": true, - "acceptedItems": [], - "isMutable": true - }, - "subEntityType": "" - } - }, - "health": 987500.0000000001, - "itemDefinitionId": 1433, - "occupiedExpansionSlots": {}, - "occupiedRampSlots": {}, - "occupiedShelterSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedWallSlots": { - "1": { - "position": [ - 1794.7667236328125, 126.12247467041016, 3687.132080078125, 1 - ], - "rotation": [-1.389447808265686, 0, 0, 0], - "characterId": "0x7220b631c3b52c17", - "actorModelId": 9181, - "health": 493750.00000000006, - "placementTime": 1771057163584, - "parentObjectCharacterId": "0x65eb297ab7a59a46", - "itemDefinitionId": 1881, - "slot": "LoveShackDoor", - "ownerCharacterId": "0x99d8000df4995f1e", - "passwordHash": 3141370770, - "grantedAccess": ["0x99d8000df4995f1e"] - } - }, - "ownerCharacterId": "0x99d8000df4995f1e", - "parentObjectCharacterId": "0x0000000000000000", - "permissions": { - "0x99d8000df4995f1e": { - "characterId": "0x99d8000df4995f1e", - "characterName": "Frank Lloyd Wright", - "useContainers": true, - "build": true, - "demolish": true, - "visit": true - } - }, - "placementTime": 1771057006593, - "position": [1796.33203125, 125.24327087402344, 3689.29052734375, 1], - "rotation": [0, -1.389447808265686, 0, 0], - "slot": "" - }, - { - "_id": { - "$oid": "69976a8bf5f7edad66d5ea9a" - }, - "characterId": "0xa87e155eb95d3f31", - "actorModelId": 9130, - "eulerAngle": 0, - "freeplaceEntities": {}, - "health": 1000000, - "itemDefinitionId": 1378, - "occupiedExpansionSlots": { - "1": { - "position": [1285.861328125, 28.816099166870117, 1869.55859375, 1], - "rotation": [0, 3.1415998935699463, 0], - "characterId": "0xd55ea87868427ed8", - "actorModelId": 9492, - "health": 1000000, - "placementTime": 1771530551782, - "parentObjectCharacterId": "0xa87e155eb95d3f31", - "itemDefinitionId": 2336, - "slot": "expansion01", - "eulerAngle": 3.1415998935699463, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {}, - "permissions": { - "0xd291d539a1578b22": { - "characterId": "0xd291d539a1578b22", - "characterName": "lester", - "useContainers": true, - "build": true, - "demolish": true, - "visit": true - } - }, - "ownerCharacterId": "0xd291d539a1578b22", - "occupiedExpansionSlots": {}, - "occupiedRampSlots": {} - }, - "2": { - "position": [ - 1278.361083984375, 28.816099166870117, 1877.058837890625, 1 - ], - "rotation": [0, -1.5707999467849731, 0], - "characterId": "0xd1ea5968f1a2c62e", - "actorModelId": 9492, - "health": 1000000, - "placementTime": 1771530553499, - "parentObjectCharacterId": "0xa87e155eb95d3f31", - "itemDefinitionId": 2336, - "slot": "expansion02", - "eulerAngle": -1.5707999467849731, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {}, - "permissions": { - "0xd291d539a1578b22": { - "characterId": "0xd291d539a1578b22", - "characterName": "lester", - "useContainers": true, - "build": true, - "demolish": true, - "visit": true - } - }, - "ownerCharacterId": "0xd291d539a1578b22", - "occupiedExpansionSlots": {}, - "occupiedRampSlots": {} - }, - "3": { - "position": [1285.861328125, 28.816099166870117, 1884.558837890625, 1], - "rotation": [0, 0, 0], - "characterId": "0x5621ac16e10e9cec", - "actorModelId": 9492, - "health": 1000000, - "placementTime": 1771530553094, - "parentObjectCharacterId": "0xa87e155eb95d3f31", - "itemDefinitionId": 2336, - "slot": "expansion03", - "eulerAngle": 0, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {}, - "permissions": { - "0xd291d539a1578b22": { - "characterId": "0xd291d539a1578b22", - "characterName": "lester", - "useContainers": true, - "build": true, - "demolish": true, - "visit": true - } - }, - "ownerCharacterId": "0xd291d539a1578b22", - "occupiedExpansionSlots": {}, - "occupiedRampSlots": {} - }, - "4": { - "position": [ - 1293.361083984375, 28.816099166870117, 1877.058837890625, 1 - ], - "rotation": [0, 1.5707999467849731, 0], - "characterId": "0x256f5e4217b3f022", - "actorModelId": 9492, - "health": 1000000, - "placementTime": 1771530552618, - "parentObjectCharacterId": "0xa87e155eb95d3f31", - "itemDefinitionId": 2336, - "slot": "expansion04", - "eulerAngle": 1.5707999467849731, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {}, - "permissions": { - "0xd291d539a1578b22": { - "characterId": "0xd291d539a1578b22", - "characterName": "lester", - "useContainers": true, - "build": true, - "demolish": true, - "visit": true - } - }, - "ownerCharacterId": "0xd291d539a1578b22", - "occupiedExpansionSlots": {}, - "occupiedRampSlots": {} - } - }, - "occupiedRampSlots": {}, - "occupiedShelterSlots": { - "1": { - "position": [ - 1280.861083984375, 28.821699142456055, 1872.058837890625, 1 - ], - "rotation": [0, 1.5707963705062866, 0, 0], - "characterId": "0xcfb2dec156846b5f", - "actorModelId": 51, - "health": 1000000, - "placementTime": 1771530875721, - "parentObjectCharacterId": "0xa87e155eb95d3f31", - "itemDefinitionId": 150, - "slot": "Structure01", - "eulerAngle": 1.5707963705062866, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": { - "1": { - "position": [ - 1280.861083984375, 31.362699508666992, 1872.058837890625, 1 - ], - "rotation": [0, 1.9513027667999268, 0, 0], - "characterId": "0xe2e652358e4dced6", - "actorModelId": 9411, - "health": 1000000, - "placementTime": 1771530878765, - "parentObjectCharacterId": "0xcfb2dec156846b5f", - "itemDefinitionId": 1897, - "slot": "Roof01", - "eulerAngle": 1.9513027667999268, - "occupiedWallSlots": {}, - "occupiedUpperWallSlots": {}, - "occupiedShelterSlots": {}, - "freeplaceEntities": {} - } - }, - "freeplaceEntities": {} - } - }, - "occupiedUpperWallSlots": {}, - "occupiedWallSlots": {}, - "ownerCharacterId": "0xd291d539a1578b22", - "parentObjectCharacterId": "0x0000000000000000", - "permissions": { - "0xd291d539a1578b22": { - "characterId": "0xd291d539a1578b22", - "characterName": "lester", - "useContainers": true, - "build": true, - "demolish": true, - "visit": true - } - }, - "placementTime": 1771530547591, - "position": [1285.702392578125, 26.6875, 1877.0595703125, 1], - "rotation": [0, 0, 0, 0], - "slot": "" - } -] diff --git a/benchmarks/src/zoneStress/zone-stress.ts b/benchmarks/src/zoneStress/zone-stress.ts deleted file mode 100644 index 440843f6f3..0000000000 --- a/benchmarks/src/zoneStress/zone-stress.ts +++ /dev/null @@ -1,454 +0,0 @@ -// ====================================================================== -// Zone stress test — profiles server-side logic with fake clients, -// real construction data, bot movement, and weapon fire. -// -// Run after `npm run build`: -// npm run stress — normal run -// npm run stress-profile — with --cpu-prof (writes cpuprofile file) -// ====================================================================== - -/* eslint-disable @typescript-eslint/no-require-imports */ -process.env["DISABLE_PLUGINS"] = "true"; - -// ---- CONFIG (edit these) -------------------------------------------- -const CONFIG = { - clients: 200, - /** Multiply the 72 example bases by this factor (~72 * N total foundations) */ - baseReplications: 3, - /** Position updates per second per bot */ - moveHz: 10, - /** Weapon fire events per second per bot (AK47, no fire-rate throttle issues) */ - fireHz: 2, - durationSeconds: 60, - /** Stats print interval in ms */ - statsIntervalMs: 5000, - explosion: { - /** How many IEDs to detonate in one wave */ - count: 10000, - /** Seconds after start to trigger the wave (let bots/construction settle first) */ - delaySeconds: 10 - } -}; -// --------------------------------------------------------------------- - -const { ZoneServer2016 } = require("../../../h1z1-server"); -const { - ExplosiveEntity -} = require("../../../out/servers/ZoneServer2016/entities/explosiveentity"); -const { - WorldDataManager -} = require("../../../out/servers/ZoneServer2016/managers/worlddatamanager"); -const { createFakeCharacter } = require("../../../out/utils/test.utils"); -const { - BaseItem -} = require("../../../out/servers/ZoneServer2016/classes/baseItem"); -const { - LoadoutItem -} = require("../../../out/servers/ZoneServer2016/classes/loadoutItem"); -const { - Weapon -} = require("../../../out/servers/ZoneServer2016/classes/weapon"); -const { - ZoneClient2016 -} = require("../../../out/servers/ZoneServer2016/classes/zoneclient"); -const rawConstruction: any[] = require("../../src/zoneStress/construction.json"); - -// Items.WEAPON_AK47 = 2229, LoadoutSlots.PRIMARY = 1 -const WEAPON_AK47 = 2229; -const SLOT_PRIMARY = 1; - -// ---- Construction replication --------------------------------------- - -function deepReplicateBase( - server: any, - base: any, - offsetX: number, - offsetZ: number -): any { - const cloned = JSON.parse(JSON.stringify(base)); - - function remapEntity(entity: any, newParentId: string) { - entity.characterId = server.generateGuid(); - entity.parentObjectCharacterId = newParentId; - if (Array.isArray(entity.position)) { - entity.position[0] += offsetX; - entity.position[2] += offsetZ; - } - for (const slotGroup of [ - "occupiedWallSlots", - "occupiedShelterSlots", - "occupiedExpansionSlots", - "occupiedRampSlots", - "occupiedUpperWallSlots", - "freeplaceEntities" - ]) { - for (const key of Object.keys(entity[slotGroup] ?? {})) { - remapEntity(entity[slotGroup][key], entity.characterId); - } - } - } - - remapEntity(cloned, "0x0000000000000000"); - return cloned; -} - -// ---- Bot helpers ---------------------------------------------------- - -let nextSessionId = 10000; - -function createBot(server: any) { - const character = createFakeCharacter(server); - - const sessionId = nextSessionId++; - const client = new ZoneClient2016( - sessionId, - `bot_${sessionId}`, - `bot_${sessionId}`, - character.characterId, - character.transientId, - server - ); - server._clients[sessionId] = client; - - // Give AK47 with unlimited ammo - const guid = server.generateGuid(); - const baseItem = new BaseItem(WEAPON_AK47, guid, 100, 1); - const loadoutItem = new LoadoutItem( - baseItem, - SLOT_PRIMARY, - character.characterId - ); - loadoutItem.weapon = new Weapon(baseItem, 9999); - character._loadout[SLOT_PRIMARY] = loadoutItem; - character.currentLoadoutSlot = SLOT_PRIMARY; - - // Initialise state objects normally set during the full login flow - if (!character.state) character.state = {}; - if (!client.fireHints) client.fireHints = {}; - - return { client, character, weapon: loadoutItem }; -} - -function randomPosition(cx: number, cz: number, radius: number): Float32Array { - const angle = Math.random() * Math.PI * 2; - const dist = Math.random() * radius; - return new Float32Array([ - cx + Math.cos(angle) * dist, - 15, - cz + Math.sin(angle) * dist, - 1 - ]); -} - -// ---- Timing helpers ------------------------------------------------- - -/** Rolling window of the last N samples */ -class RollingStats { - private samples: number[] = []; - constructor(private readonly window: number = 20) {} - push(v: number) { - this.samples.push(v); - if (this.samples.length > this.window) this.samples.shift(); - } - avg() { - return this.samples.length === 0 - ? 0 - : this.samples.reduce((a, b) => a + b, 0) / this.samples.length; - } - max() { - return this.samples.length === 0 ? 0 : Math.max(...this.samples); - } -} - -// ---- Explosion wave ------------------------------------------------- - -// Items.IED = 146, actor model 9176 (matches commands.ts placement) -const ITEMS_IED = 146; -const IED_MODEL = 9176; - -/** Waits for the explosion manager's queue to fully drain. */ -function waitForExplosionFlush(server: any): Promise { - return new Promise((resolve) => { - const mgr = server.explosionManager as any; - function check() { - if (mgr._pending.length === 0 && !mgr._scheduled) resolve(); - else setImmediate(check); - } - setImmediate(check); - }); -} - -async function runExplosionWave( - server: any, - count: number, - pos: Float32Array -): Promise { - console.log( - `\n[EXPLOSION] Creating ${count} IEDs at (${pos[0].toFixed(0)}, ${pos[1].toFixed(0)}, ${pos[2].toFixed(0)})…` - ); - - const t0 = performance.now(); - - for (let i = 0; i < count; i++) { - const id = server.generateGuid(); - const exp = new ExplosiveEntity( - id, - server.getTransientId(id), - IED_MODEL, - new Float32Array([pos[0], pos[1], pos[2], 1]), - new Float32Array([0, 0, 0, 0]), - server, - ITEMS_IED, - "stress-test" - ); - // Mirror what detonate() does before calling queueExplosion so already-queued - // IEDs are skipped in the grid scan and don't chain-react with each other. - exp.detonated = true; - server._explosives[id] = exp; - server.explosionManager.queueExplosion(exp); - } - - const queueMs = performance.now() - t0; - console.log( - `[EXPLOSION] Queued in ${queueMs.toFixed(1)}ms — waiting for chunks to drain…` - ); - - await waitForExplosionFlush(server); - - const totalMs = performance.now() - t0; - const chunks = Math.ceil(count / 200); - console.log( - `[EXPLOSION] Done — total ${totalMs.toFixed(0)}ms over ${chunks} chunks (~${(totalMs / chunks).toFixed(1)}ms/chunk)` - ); -} - -// ---- Main ----------------------------------------------------------- - -async function main() { - console.log("=== H1Z1 Zone Stress Test ==="); - console.log( - `Config: ${CONFIG.clients} clients | ${rawConstruction.length} base templates x ${CONFIG.baseReplications} replications` + - ` | move@${CONFIG.moveHz}Hz | fire@${CONFIG.fireHz}Hz | ${CONFIG.durationSeconds}s` - ); - - const server = new ZoneServer2016(0); - - console.log("Starting server (no network, no MongoDB)…"); - await server.start(); - - // Stub out sendData — fake clients have no real UDP connection so calling the - // real one would error. We measure dispatch latency via wall-clock timing instead. - server.sendData = () => {}; - server.sendUnbufferedData = () => {}; - server.sendOrderedData = () => {}; - - // --- Load construction --- - console.log(`Replicating construction ${CONFIG.baseReplications}x…`); - const allBases: any[] = [...rawConstruction]; - for (let rep = 1; rep < CONFIG.baseReplications; rep++) { - for (const base of rawConstruction) { - allBases.push(deepReplicateBase(server, base, rep * 700, 0)); - } - } - WorldDataManager.loadConstructionParentEntities(allBases, server); - const foundationCount = Object.keys( - server._constructionFoundations ?? {} - ).length; - const simpleCount = Object.keys(server._constructionSimple ?? {}).length; - const doorCount = Object.keys(server._constructionDoors ?? {}).length; - console.log( - `Construction loaded: ${foundationCount} foundations, ${simpleCount} pieces, ${doorCount} doors` - ); - - // --- Create bots --- - console.log(`Creating ${CONFIG.clients} bots…`); - // Derive spawn center as the centroid of all replicated base positions - const CENTER_X = Math.round( - allBases.reduce((sum: number, b: any) => sum + (b.position?.[0] ?? 0), 0) / - allBases.length - ); - const CENTER_Z = Math.round( - allBases.reduce((sum: number, b: any) => sum + (b.position?.[2] ?? 0), 0) / - allBases.length - ); - console.log( - `Bot spawn center derived from bases: (${CENTER_X}, ${CENTER_Z})` - ); - - const bots: { - client: any; - character: any; - weapon: any; - pos: Float32Array; - projCount: number; - }[] = []; - - for (let i = 0; i < CONFIG.clients; i++) { - const { client, character, weapon } = createBot(server); - const pos = randomPosition(CENTER_X, CENTER_Z, 500); - character.state.position = pos; - character.state.yaw = Math.random() * Math.PI * 2; - bots.push({ client, character, weapon, pos, projCount: 0 }); - } - console.log("Bots created. Running stress loop…\n"); - - // Explosion wave — triggers after delay so construction & bots are in place - setTimeout( - () => - void runExplosionWave( - server, - CONFIG.explosion.count, - new Float32Array([CENTER_X, 15, CENTER_Z, 1]) - ), - CONFIG.explosion.delaySeconds * 1000 - ); - - // --- Metrics --- - let totalMovePkts = 0; - let totalFirePkts = 0; - let moveBatchMs = 0; // cumulative ms spent processing movement - let fireBatchMs = 0; - const moveStats = new RollingStats(); - const fireStats = new RollingStats(); - - const startTime = Date.now(); - const statRows: { - t: number; - moveAvgMs: number; - fireAvgMs: number; - memMB: number; - }[] = []; - - // --- Movement loop --- - const moveIntervalMs = 1000 / CONFIG.moveHz; - const moveTimer = setInterval(() => { - const now = Date.now(); - const t0 = performance.now(); - - for (const bot of bots) { - bot.pos[0] += (Math.random() - 0.5) * 2; - bot.pos[2] += (Math.random() - 0.5) * 2; - bot.character.state.position = bot.pos; - - server._packetHandlers.PlayerUpdatePosition(server, bot.client, { - data: { - flags: 0, - sequenceTime: now & 0xffffffff, - position: bot.pos, - rotation: new Float32Array([0, 0, 0, 0]), - orientation: 0, - rotationRaw: new Float32Array([0, 0, 0, 0]), - lookAt: new Float32Array([0, 0, 0]), - stance: 256 // ON_GROUND - } - }); - totalMovePkts++; - } - - const dt = performance.now() - t0; - moveBatchMs += dt; - moveStats.push(dt); - }, moveIntervalMs); - - // --- Fire loop --- - const fireIntervalMs = 1000 / CONFIG.fireHz; - const fireTimer = setInterval(() => { - const t0 = performance.now(); - - for (const bot of bots) { - if (!bot.weapon.weapon) continue; - if (bot.weapon.weapon.ammoCount <= 0) bot.weapon.weapon.ammoCount = 9999; - - server.handleWeaponFire(bot.client, bot.weapon, { - gameTime: Date.now() & 0xffffffff, - packet: { - position: bot.pos, - sessionProjectileCount: bot.projCount++, - rotation: new Float32Array([0, 0, 0, 0]) - } - }); - totalFirePkts++; - } - - const dt = performance.now() - t0; - fireBatchMs += dt; - fireStats.push(dt); - }, fireIntervalMs); - - // --- Stats loop --- - const statsTimer = setInterval(() => { - const t = Math.round((Date.now() - startTime) / 1000); - const memMB = Math.round(process.memoryUsage().rss / 1024 / 1024); - - // ms per batch (200-bot batch), and derived packets-per-second - const mAvg = moveStats.avg(); - const fAvg = fireStats.avg(); - const movePktsPerSec = - mAvg > 0 ? Math.round((bots.length / mAvg) * 1000) : 0; - const firePktsPerSec = - fAvg > 0 ? Math.round((bots.length / fAvg) * 1000) : 0; - - statRows.push({ t, moveAvgMs: mAvg, fireAvgMs: fAvg, memMB }); - - console.log( - `[${String(t).padStart(3)}s]` + - ` move batch=${mAvg.toFixed(2).padStart(7)}ms (~${String(movePktsPerSec).padStart(6)}/s)` + - ` fire batch=${fAvg.toFixed(2).padStart(7)}ms (~${String(firePktsPerSec).padStart(6)}/s)` + - ` mem=${memMB}MB` - ); - }, CONFIG.statsIntervalMs); - - // --- Shutdown --- - setTimeout(async () => { - clearInterval(moveTimer); - clearInterval(fireTimer); - clearInterval(statsTimer); - - const elapsed = (Date.now() - startTime) / 1000; - const memPeak = statRows.length - ? Math.max(...statRows.map((r) => r.memMB)) - : 0; - const memAvg = statRows.length - ? Math.round(statRows.reduce((a, r) => a + r.memMB, 0) / statRows.length) - : 0; - const movePeak = statRows.length - ? Math.max(...statRows.map((r) => r.moveAvgMs)).toFixed(2) - : "0"; - const firePeak = statRows.length - ? Math.max(...statRows.map((r) => r.fireAvgMs)).toFixed(2) - : "0"; - - const totalMoveBudgetMs = elapsed * 1000; // 1 batch per ms at 1kHz theoretical max - const moveCpuPct = ((moveBatchMs / totalMoveBudgetMs) * 100).toFixed(1); - const fireCpuPct = ((fireBatchMs / totalMoveBudgetMs) * 100).toFixed(1); - - console.log("\n=== FINAL REPORT ==="); - console.log(`Duration: ${elapsed.toFixed(1)}s`); - console.log(`Clients: ${CONFIG.clients}`); - console.log(`Foundations loaded: ${foundationCount}`); - console.log(`Construction pieces: ${simpleCount + doorCount}`); - console.log(`Move packets total: ${totalMovePkts}`); - console.log(`Fire events total: ${totalFirePkts}`); - console.log( - `Move batch peak: ${movePeak}ms (${moveCpuPct}% of wall time)` - ); - console.log( - `Fire batch peak: ${firePeak}ms (${fireCpuPct}% of wall time)` - ); - console.log(`Memory avg / peak: ${memAvg}MB / ${memPeak}MB`); - - if (process.execArgv.some((a) => a.startsWith("--cpu-prof"))) { - console.log( - "\nCPU profile written — open .cpuprofile in Chrome DevTools > Performance > Load profile" - ); - } - - await server.stop(); - process.exit(0); - }, CONFIG.durationSeconds * 1000); -} - -main().catch((err) => { - console.error("Stress test crashed:", err); - process.exit(1); -}); diff --git a/bin/2016/zoneServer.js b/bin/2016/zoneServer.js index 0696160ee1..c2f7e1d06f 100644 --- a/bin/2016/zoneServer.js +++ b/bin/2016/zoneServer.js @@ -1,13 +1,5 @@ -if (process.env.APM_ENABLED == "true") { - require("elastic-apm-node").start({ - serviceName: process.env.ELASTIC_APM_SERVICE_NAME, - secretToken: process.env.ELASTIC_APM_SECRET_TOKEN, - serverUrl: process.env.ELASTIC_APM_SERVER_URL, - environment: process.env.ELASTIC_APM_ENVIRONMENT, - }); -} - const { ZoneServer2016 } = require("../../h1z1-server"); + const Zone = new ZoneServer2016( 1117, Buffer.from("F70IaxuU8C/w7FPXY1ibXw==", "base64") diff --git a/data/2016/dataSources/AllowedFileHashes.json b/data/2016/dataSources/AllowedFileHashes.json index 9865509090..8697af4e23 100644 --- a/data/2016/dataSources/AllowedFileHashes.json +++ b/data/2016/dataSources/AllowedFileHashes.json @@ -1025,7 +1025,7 @@ }, { "file_name": "Assets_256.pack", - "crc32_hash": "5fd58d48" + "crc32_hash": "396f3599" }, { "file_name": "Assets_257.pack", @@ -1037,6 +1037,7 @@ }, { "file_name": "Assets_259.pack", - "crc32_hash": "03c27f8e" + "old_crc32_hash": "bfdc08a2", + "crc32_hash": "a20404c1" } ] diff --git a/data/2016/dataSources/LoginData.json b/data/2016/dataSources/LoginData.json index 64ea338cfe..66cd379354 100644 --- a/data/2016/dataSources/LoginData.json +++ b/data/2016/dataSources/LoginData.json @@ -3268,7 +3268,7 @@ "unknownString6": "LVS", "unknownString7": "" }, - "errorDetails": [], + "unknownArray2": [], "unknownString2": "", "unknownString3": "", "unknownBoolean3": false, diff --git a/data/2016/lootTables/containers/Armoire.json b/data/2016/lootTables/containers/Armoire.json deleted file mode 100644 index 71493c8b60..0000000000 --- a/data/2016/lootTables/containers/Armoire.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { - "min": 0, - "max": 3 - }, - "entries": [ - { - "item": 2088, - "weight": 50, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2177, - "weight": 50, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 23, - "weight": 50, - "count": { - "min": 1, - "max": 4 - } - }, - { - "item": 2102, - "weight": 50, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 142, - "weight": 50, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1803, - "weight": 60, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2255, - "weight": 60, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Builder.json b/data/2016/lootTables/containers/Builder.json deleted file mode 100644 index af7705478d..0000000000 --- a/data/2016/lootTables/containers/Builder.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { - "min": 1, - "max": 1 - }, - "entries": [ - { - "item": 48, - "weight": 1, - "count": { - "min": 50, - "max": 50 - } - }, - { - "item": 16, - "weight": 1, - "count": { - "min": 50, - "max": 50 - } - }, - { - "item": 58, - "weight": 1, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 82, - "weight": 1, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1536, - "weight": 1, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1433, - "weight": 1, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1891, - "weight": 1, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1441, - "weight": 1, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Cabinets.json b/data/2016/lootTables/containers/Cabinets.json deleted file mode 100644 index 207271f551..0000000000 --- a/data/2016/lootTables/containers/Cabinets.json +++ /dev/null @@ -1,174 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { - "min": 0, - "max": 2 - }, - "entries": [ - { - "item": 23, - "weight": 15, - "count": { - "min": 1, - "max": 2 - } - }, - { - "item": 1436, - "weight": 10, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1428, - "weight": 15, - "count": { - "min": 1, - "max": 2 - } - }, - { - "item": 22, - "weight": 13, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 7, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1393, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1394, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1391, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1392, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1396, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1397, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1398, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1399, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1400, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1401, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1395, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1431, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2536, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 3460, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - } - ] - }, - { - "conditions": [{ "condition": "poi_names", "poi_names": ["Pleasant Valley"] }], - "rolls": { "min": 0, "max": 1 }, - "entries": [ - { "type": "loot_table", "table": "sub/Cabinets_pv", "weight": 70 }, - { "type": "empty", "weight": 30 } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Cabinets_Bathroom.json b/data/2016/lootTables/containers/Cabinets_Bathroom.json deleted file mode 100644 index 24bff66eda..0000000000 --- a/data/2016/lootTables/containers/Cabinets_Bathroom.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { - "min": 0, - "max": 3 - }, - "entries": [ - { - "item": 1742, - "weight": 45, - "count": { - "min": 1, - "max": 2 - } - }, - { - "item": 1388, - "weight": 45, - "count": { - "min": 1, - "max": 2 - } - }, - { - "item": 1471, - "weight": 45, - "count": { - "min": 1, - "max": 2 - } - }, - { - "item": 1512, - "weight": 45, - "count": { - "min": 1, - "max": 2 - } - }, - { - "item": 24, - "weight": 25, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 78, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Cabinets_Cube.json b/data/2016/lootTables/containers/Cabinets_Cube.json deleted file mode 100644 index d19c660608..0000000000 --- a/data/2016/lootTables/containers/Cabinets_Cube.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { - "min": 0, - "max": 1 - }, - "entries": [ - { - "item": 22, - "weight": 13, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1436, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 3460, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 7, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1393, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - } - ] - }, - { - "conditions": [{ "condition": "poi_names", "poi_names": ["Pleasant Valley"] }], - "rolls": { "min": 0, "max": 1 }, - "entries": [ - { "item": 57, "weight": 35 }, - { "item": 56, "weight": 35 }, - { "type": "empty", "weight": 30 } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Cabinets_Kitchen.json b/data/2016/lootTables/containers/Cabinets_Kitchen.json deleted file mode 100644 index 0c6bb54fe0..0000000000 --- a/data/2016/lootTables/containers/Cabinets_Kitchen.json +++ /dev/null @@ -1,166 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { - "min": 0, - "max": 3 - }, - "entries": [ - { - "item": 22, - "weight": 15, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 57, - "weight": 10, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 56, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1987, - "weight": 25, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1988, - "weight": 25, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 7, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1393, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1394, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1391, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1392, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1396, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1397, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1398, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1399, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1400, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1401, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1395, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1431, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2536, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Crate.json b/data/2016/lootTables/containers/Crate.json deleted file mode 100644 index 4a55cedf03..0000000000 --- a/data/2016/lootTables/containers/Crate.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "type": "container", - "spawnChance": 50, - "pools": [ - { - "conditions": [], - "rolls": { - "min": 1, - "max": 1 - }, - "entries": [ - { - "item": 7, - "weight": 20, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1368, - "weight": 20, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 23, - "weight": 20, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 25, - "weight": 6, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Demolitioner.json b/data/2016/lootTables/containers/Demolitioner.json deleted file mode 100644 index 6f911488d0..0000000000 --- a/data/2016/lootTables/containers/Demolitioner.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { - "min": 1, - "max": 1 - }, - "entries": [ - { - "item": 1903, - "weight": 1, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1699, - "weight": 1, - "count": { - "min": 10, - "max": 20 - } - }, - { - "item": 1384, - "weight": 1, - "count": { - "min": 10, - "max": 20 - } - }, - { - "item": 1436, - "weight": 1, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1441, - "weight": 1, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Desk.json b/data/2016/lootTables/containers/Desk.json deleted file mode 100644 index 5471355eee..0000000000 --- a/data/2016/lootTables/containers/Desk.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { "min": 0, "max": 2 }, - "entries": [ - { "item": 23, "weight": 30, "count": { "min": 1, "max": 2 } }, - { "item": 1441, "weight": 5 }, - { "item": 3460, "weight": 3 }, - { "type": "loot_table", "table": "sub/Desk_common", "weight": 40 }, - { "type": "empty", "weight": 20 } - ] - }, - { - "conditions": [{ "condition": "poi_tag", "tags": ["residential"] }], - "rolls": { "min": 0, "max": 1 }, - "entries": [ - { "type": "loot_table", "table": "sub/Desk_residential", "weight": 70 }, - { "type": "empty", "weight": 30 } - ] - }, - { - "conditions": [{ "condition": "poi_names", "poi_names": ["De Soto Service Stop"] }], - "rolls": { "min": 0, "max": 1 }, - "entries": [ - { "type": "loot_table", "table": "sub/Desk_desoto", "weight": 75 }, - { "type": "empty", "weight": 25 } - ] - }, - { - "conditions": [{ "condition": "poi_tag", "tags": ["military"] }], - "rolls": { "min": 0, "max": 2 }, - "entries": [ - { "type": "loot_table", "table": "sub/Desk_military", "weight": 75 }, - { "type": "empty", "weight": 25 } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Dresser.json b/data/2016/lootTables/containers/Dresser.json deleted file mode 100644 index ac2d5f08d8..0000000000 --- a/data/2016/lootTables/containers/Dresser.json +++ /dev/null @@ -1,94 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { - "min": 0, - "max": 3 - }, - "entries": [ - { - "item": 2088, - "weight": 50, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2177, - "weight": 50, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 23, - "weight": 50, - "count": { - "min": 1, - "max": 4 - } - }, - { - "item": 2102, - "weight": 50, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 142, - "weight": 50, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 3460, - "weight": 3, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1441, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2255, - "weight": 50, - "count": { - "min": 1, - "max": 1 - } - } - ] - }, - { - "conditions": [], - "rolls": { "min": 0, "max": 1 }, - "entries": [ - { "type": "loot_table", "table": "sub/Dresser_clothing", "weight": 70 }, - { "type": "empty", "weight": 30 } - ] - }, - { - "conditions": [{ "condition": "poi_tag", "tags": ["residential"] }], - "rolls": { "min": 0, "max": 1 }, - "entries": [ - { "type": "loot_table", "table": "sub/Dresser_residential", "weight": 65 }, - { "type": "empty", "weight": 35 } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Drug_Cabinets.json b/data/2016/lootTables/containers/Drug_Cabinets.json deleted file mode 100644 index ddcf789d5f..0000000000 --- a/data/2016/lootTables/containers/Drug_Cabinets.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { - "min": 0, - "max": 2 - }, - "entries": [ - { - "item": 78, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1751, - "weight": 30, - "count": { - "min": 1, - "max": 3 - } - }, - { - "item": 2214, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 24, - "weight": 30, - "count": { - "min": 1, - "max": 2 - } - }, - { - "item": 77, - "weight": 20, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1508, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1512, - "weight": 30, - "count": { - "min": 1, - "max": 2 - } - }, - { - "item": 1388, - "weight": 30, - "count": { - "min": 1, - "max": 2 - } - }, - { - "item": 2641, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2498, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1471, - "weight": 30, - "count": { - "min": 1, - "max": 2 - } - } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Dryer.json b/data/2016/lootTables/containers/Dryer.json deleted file mode 100644 index 6e54b2926b..0000000000 --- a/data/2016/lootTables/containers/Dryer.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { - "min": 0, - "max": 2 - }, - "entries": [ - { - "item": 2088, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2177, - "weight": 20, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2373, - "weight": 10, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 23, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Dumpster.json b/data/2016/lootTables/containers/Dumpster.json deleted file mode 100644 index a2005b41e6..0000000000 --- a/data/2016/lootTables/containers/Dumpster.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { - "min": 0, - "max": 3 - }, - "entries": [ - { - "item": 23, - "weight": 50, - "count": { - "min": 1, - "max": 3 - } - }, - { - "item": 142, - "weight": 25, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 25, - "weight": 13, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 26, - "weight": 25, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1353, - "weight": 25, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 72, - "weight": 25, - "count": { - "min": 1, - "max": 2 - } - } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Farmer.json b/data/2016/lootTables/containers/Farmer.json deleted file mode 100644 index 24e2a64d8f..0000000000 --- a/data/2016/lootTables/containers/Farmer.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { - "min": 1, - "max": 1 - }, - "entries": [ - { - "item": 1383, - "weight": 1, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1383, - "weight": 1, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1383, - "weight": 1, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1383, - "weight": 1, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 124, - "weight": 1, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1987, - "weight": 1, - "count": { - "min": 10, - "max": 10 - } - }, - { - "item": 1988, - "weight": 1, - "count": { - "min": 10, - "max": 10 - } - }, - { - "item": 25, - "weight": 1, - "count": { - "min": 10, - "max": 15 - } - }, - { - "item": 1441, - "weight": 1, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Fighter.json b/data/2016/lootTables/containers/Fighter.json deleted file mode 100644 index 8d073eaa73..0000000000 --- a/data/2016/lootTables/containers/Fighter.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { - "min": 1, - "max": 1 - }, - "entries": [ - { - "item": 1374, - "weight": 1, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2229, - "weight": 1, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2325, - "weight": 1, - "count": { - "min": 50, - "max": 50 - } - }, - { - "item": 1511, - "weight": 1, - "count": { - "min": 15, - "max": 15 - } - }, - { - "item": 1441, - "weight": 1, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/File_Cabinet.json b/data/2016/lootTables/containers/File_Cabinet.json deleted file mode 100644 index a63f33f8fc..0000000000 --- a/data/2016/lootTables/containers/File_Cabinet.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { "min": 0, "max": 3 }, - "entries": [ - { "type": "loot_table", "table": "sub/File_junk", "weight": 70 }, - { "type": "loot_table", "table": "sub/File_uncommon", "weight": 12 }, - { "type": "loot_table", "table": "sub/File_rare", "weight": 1 }, - { "type": "empty", "weight": 17 } - ] - }, - { - "conditions": [{ "condition": "poi_tag", "tags": ["residential"] }], - "rolls": { "min": 0, "max": 1 }, - "entries": [ - { "type": "loot_table", "table": "sub/File_residential", "weight": 65 }, - { "type": "empty", "weight": 35 } - ] - }, - { - "conditions": [{ "condition": "poi_tag", "tags": ["military"] }], - "rolls": { "min": 0, "max": 2 }, - "entries": [ - { "type": "loot_table", "table": "sub/File_military", "weight": 80 }, - { "type": "empty", "weight": 20 } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Fridge.json b/data/2016/lootTables/containers/Fridge.json deleted file mode 100644 index 108b154743..0000000000 --- a/data/2016/lootTables/containers/Fridge.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { "min": 0, "max": 2 }, - "entries": [ - { "item": 7, "weight": 50 }, - { "item": 3075, "weight": 15 }, - { "item": 20, "weight": 25 }, - { "item": 1535, "weight": 20 }, - { "item": 1381, "weight": 25 }, - { "type": "empty", "weight": 30 } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Garbage_Can.json b/data/2016/lootTables/containers/Garbage_Can.json deleted file mode 100644 index a2005b41e6..0000000000 --- a/data/2016/lootTables/containers/Garbage_Can.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { - "min": 0, - "max": 3 - }, - "entries": [ - { - "item": 23, - "weight": 50, - "count": { - "min": 1, - "max": 3 - } - }, - { - "item": 142, - "weight": 25, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 25, - "weight": 13, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 26, - "weight": 25, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1353, - "weight": 25, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 72, - "weight": 25, - "count": { - "min": 1, - "max": 2 - } - } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Grossing_Station.json b/data/2016/lootTables/containers/Grossing_Station.json deleted file mode 100644 index f59c4abc7e..0000000000 --- a/data/2016/lootTables/containers/Grossing_Station.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { - "min": 0, - "max": 2 - }, - "entries": [ - { - "item": 1464, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1510, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Hospital.json b/data/2016/lootTables/containers/Hospital.json deleted file mode 100644 index 2382e3d3f4..0000000000 --- a/data/2016/lootTables/containers/Hospital.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { - "min": 1, - "max": 1 - }, - "entries": [ - { - "item": 24, - "weight": 1, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1428, - "weight": 1, - "count": { - "min": 6, - "max": 12 - } - }, - { - "item": 1511, - "weight": 1, - "count": { - "min": 4, - "max": 8 - } - }, - { - "item": 2509, - "weight": 1, - "count": { - "min": 1, - "max": 2 - } - }, - { - "item": 2512, - "weight": 1, - "count": { - "min": 1, - "max": 2 - } - } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Hospital_Cabinets.json b/data/2016/lootTables/containers/Hospital_Cabinets.json deleted file mode 100644 index 9a502edd0e..0000000000 --- a/data/2016/lootTables/containers/Hospital_Cabinets.json +++ /dev/null @@ -1,118 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { - "min": 0, - "max": 3 - }, - "entries": [ - { - "item": 1751, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 3076, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2637, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2703, - "weight": 14, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2633, - "weight": 14, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2632, - "weight": 14, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2214, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 24, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1508, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2641, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2498, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1512, - "weight": 30, - "count": { - "min": 1, - "max": 3 - } - }, - { - "item": 2612, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Hospital_Desk.json b/data/2016/lootTables/containers/Hospital_Desk.json deleted file mode 100644 index 7252b812f8..0000000000 --- a/data/2016/lootTables/containers/Hospital_Desk.json +++ /dev/null @@ -1,110 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { - "min": 0, - "max": 3 - }, - "entries": [ - { - "item": 2641, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2498, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1751, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2214, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 24, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1508, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1512, - "weight": 30, - "count": { - "min": 1, - "max": 2 - } - }, - { - "item": 1388, - "weight": 30, - "count": { - "min": 1, - "max": 2 - } - }, - { - "item": 1471, - "weight": 30, - "count": { - "min": 1, - "max": 2 - } - }, - { - "item": 2610, - "weight": 25, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2635, - "weight": 23, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2631, - "weight": 23, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Hospital_Refrigerator.json b/data/2016/lootTables/containers/Hospital_Refrigerator.json deleted file mode 100644 index 2beaab2f32..0000000000 --- a/data/2016/lootTables/containers/Hospital_Refrigerator.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { - "min": 0, - "max": 2 - }, - "entries": [ - { - "item": 1464, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 3075, - "weight": 20, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1510, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Locker.json b/data/2016/lootTables/containers/Locker.json deleted file mode 100644 index 63130467d3..0000000000 --- a/data/2016/lootTables/containers/Locker.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [ - { "condition": "poi_tag", "tags": ["military"] } - ], - "rolls": { "min": 0, "max": 1 }, - "entries": [ - { "item": 2124, "weight": 30, "count": { "min": 1, "max": 1 } }, - { "item": 2271, "weight": 25, "count": { "min": 1, "max": 1 } }, - { "item": 2172, "weight": 20, "count": { "min": 1, "max": 1 } }, - { "item": 10, "weight": 10, "count": { "min": 1, "max": 1 } }, - { "item": 2229, "weight": 8, "count": { "min": 1, "max": 1 } } - ] - }, - { - "conditions": [ - { "condition": "not_poi_tag", "tags": ["high_tier", "military"] } - ], - "rolls": { "min": 0, "max": 1 }, - "entries": [ - { "item": 2038, "weight": 40, "count": { "min": 1, "max": 1 } }, - { "item": 2, "weight": 25, "count": { "min": 1, "max": 1 } }, - { "item": 1997, "weight": 25, "count": { "min": 1, "max": 1 } }, - { "item": 2172, "weight": 10, "count": { "min": 1, "max": 1 } } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Medic.json b/data/2016/lootTables/containers/Medic.json deleted file mode 100644 index 872f1cfc42..0000000000 --- a/data/2016/lootTables/containers/Medic.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { - "min": 1, - "max": 1 - }, - "entries": [ - { - "item": 1751, - "weight": 1, - "count": { - "min": 25, - "max": 25 - } - }, - { - "item": 2214, - "weight": 1, - "count": { - "min": 15, - "max": 15 - } - }, - { - "item": 78, - "weight": 1, - "count": { - "min": 10, - "max": 10 - } - }, - { - "item": 2645, - "weight": 1, - "count": { - "min": 5, - "max": 5 - } - }, - { - "item": 2646, - "weight": 1, - "count": { - "min": 5, - "max": 5 - } - }, - { - "item": 2647, - "weight": 1, - "count": { - "min": 5, - "max": 5 - } - }, - { - "item": 2648, - "weight": 1, - "count": { - "min": 5, - "max": 5 - } - }, - { - "item": 1441, - "weight": 1, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Medical_Station.json b/data/2016/lootTables/containers/Medical_Station.json deleted file mode 100644 index ddcf789d5f..0000000000 --- a/data/2016/lootTables/containers/Medical_Station.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { - "min": 0, - "max": 2 - }, - "entries": [ - { - "item": 78, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1751, - "weight": 30, - "count": { - "min": 1, - "max": 3 - } - }, - { - "item": 2214, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 24, - "weight": 30, - "count": { - "min": 1, - "max": 2 - } - }, - { - "item": 77, - "weight": 20, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1508, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1512, - "weight": 30, - "count": { - "min": 1, - "max": 2 - } - }, - { - "item": 1388, - "weight": 30, - "count": { - "min": 1, - "max": 2 - } - }, - { - "item": 2641, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2498, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1471, - "weight": 30, - "count": { - "min": 1, - "max": 2 - } - } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Ottoman.json b/data/2016/lootTables/containers/Ottoman.json deleted file mode 100644 index 58b71081f4..0000000000 --- a/data/2016/lootTables/containers/Ottoman.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { "min": 0, "max": 3 }, - "entries": [ - { "item": 2088, "weight": 30 }, - { "item": 2177, "weight": 30 }, - { "item": 23, "weight": 20, "count": { "min": 1, "max": 4 } }, - { "item": 2102, "weight": 25 }, - { "item": 1380, "weight": 15 }, - { "item": 142, "weight": 25 }, - { "item": 3460, "weight": 3 }, - { "item": 2255, "weight": 25 }, - { "type": "empty", "weight": 40 } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Supplier.json b/data/2016/lootTables/containers/Supplier.json deleted file mode 100644 index f31df1764b..0000000000 --- a/data/2016/lootTables/containers/Supplier.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { - "min": 1, - "max": 1 - }, - "entries": [ - { - "item": 1460, - "weight": 1, - "count": { - "min": 3, - "max": 3 - } - }, - { - "item": 1459, - "weight": 1, - "count": { - "min": 3, - "max": 3 - } - }, - { - "item": 1457, - "weight": 1, - "count": { - "min": 3, - "max": 3 - } - }, - { - "item": 1458, - "weight": 1, - "count": { - "min": 3, - "max": 3 - } - }, - { - "item": 1371, - "weight": 1, - "count": { - "min": 15, - "max": 15 - } - }, - { - "item": 1386, - "weight": 1, - "count": { - "min": 10, - "max": 10 - } - }, - { - "item": 110, - "weight": 1, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1441, - "weight": 1, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Tool_Cabinet.json b/data/2016/lootTables/containers/Tool_Cabinet.json deleted file mode 100644 index 15f174785a..0000000000 --- a/data/2016/lootTables/containers/Tool_Cabinet.json +++ /dev/null @@ -1,62 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { - "min": 0, - "max": 3 - }, - "entries": [ - { - "item": 82, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 47, - "weight": 37, - "count": { - "min": 1, - "max": 2 - } - }, - { - "item": 1448, - "weight": 32, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1436, - "weight": 8, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1538, - "weight": 35, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1903, - "weight": 25, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Washer.json b/data/2016/lootTables/containers/Washer.json deleted file mode 100644 index 6e54b2926b..0000000000 --- a/data/2016/lootTables/containers/Washer.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { - "min": 0, - "max": 2 - }, - "entries": [ - { - "item": 2088, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2177, - "weight": 20, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2373, - "weight": 10, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 23, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Weapons_Locker.json b/data/2016/lootTables/containers/Weapons_Locker.json deleted file mode 100644 index c9ad447baf..0000000000 --- a/data/2016/lootTables/containers/Weapons_Locker.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [ - { "condition": "poi_tag", "tags": ["high_tier", "military"] } - ], - "rolls": { "min": 0, "max": 1 }, - "entries": [ - { "item": 10, "weight": 15, "count": { "min": 1, "max": 1 } }, - { "item": 2229, "weight": 12, "count": { "min": 1, "max": 1 } }, - { "item": 1373, "weight": 8, "count": { "min": 1, "max": 1 } }, - { "item": 2271, "weight": 30, "count": { "min": 1, "max": 1 } }, - { "item": 2124, "weight": 25, "count": { "min": 1, "max": 1 } } - ] - }, - { - "conditions": [ - { "condition": "not_poi_tag", "tags": ["high_tier", "military"] } - ], - "rolls": { "min": 0, "max": 1 }, - "entries": [ - { "item": 2, "weight": 35, "count": { "min": 1, "max": 1 } }, - { "item": 1997, "weight": 35, "count": { "min": 1, "max": 1 } }, - { "item": 1374, "weight": 15, "count": { "min": 1, "max": 1 } }, - { "item": 2038, "weight": 15, "count": { "min": 1, "max": 1 } } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Wrecked_Car.json b/data/2016/lootTables/containers/Wrecked_Car.json deleted file mode 100644 index d58ccf2015..0000000000 --- a/data/2016/lootTables/containers/Wrecked_Car.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { "min": 0, "max": 3 }, - "entries": [ - { "item": 48, "weight": 30, "count": { "min": 1, "max": 3 } }, - { "item": 46, "weight": 25, "count": { "min": 1, "max": 3 } }, - { "item": 155, "weight": 20 }, - { "item": 1895, "weight": 12 }, - { "item": 1467, "weight": 10 }, - { "type": "empty", "weight": 28 } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Wrecked_Truck.json b/data/2016/lootTables/containers/Wrecked_Truck.json deleted file mode 100644 index 819e0cac73..0000000000 --- a/data/2016/lootTables/containers/Wrecked_Truck.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { "min": 0, "max": 4 }, - "entries": [ - { "item": 48, "weight": 28, "count": { "min": 1, "max": 3 } }, - { "item": 46, "weight": 23, "count": { "min": 1, "max": 3 } }, - { "item": 155, "weight": 20 }, - { "item": 47, "weight": 12 }, - { "item": 1467, "weight": 10 }, - { "item": 1895, "weight": 10 }, - { "type": "empty", "weight": 22 } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/Wrecked_Van.json b/data/2016/lootTables/containers/Wrecked_Van.json deleted file mode 100644 index c175398ece..0000000000 --- a/data/2016/lootTables/containers/Wrecked_Van.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { "min": 0, "max": 3 }, - "entries": [ - { "item": 48, "weight": 30, "count": { "min": 1, "max": 3 } }, - { "item": 46, "weight": 25, "count": { "min": 1, "max": 3 } }, - { "item": 155, "weight": 20 }, - { "item": 1467, "weight": 12 }, - { "item": 1895, "weight": 10 }, - { "type": "empty", "weight": 28 } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/sub/Cabinets_pv.json b/data/2016/lootTables/containers/sub/Cabinets_pv.json deleted file mode 100644 index 3a56a12455..0000000000 --- a/data/2016/lootTables/containers/sub/Cabinets_pv.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { "min": 0, "max": 1 }, - "entries": [ - { "item": 2102, "weight": 15 }, - { "item": 57, "weight": 10 }, - { "item": 56, "weight": 40 }, - { "item": 2324, "weight": 15 }, - { "type": "empty", "weight": 20 } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/sub/Desk_common.json b/data/2016/lootTables/containers/sub/Desk_common.json deleted file mode 100644 index ee4cd29ad4..0000000000 --- a/data/2016/lootTables/containers/sub/Desk_common.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { "min": 0, "max": 1 }, - "entries": [ - { "item": 56, "weight": 25 }, - { "item": 1436, "weight": 20 }, - { "item": 1512, "weight": 15 }, - { "item": 1696, "weight": 15 }, - { "type": "empty", "weight": 25 } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/sub/Desk_desoto.json b/data/2016/lootTables/containers/sub/Desk_desoto.json deleted file mode 100644 index 9027772ba3..0000000000 --- a/data/2016/lootTables/containers/sub/Desk_desoto.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { "min": 0, "max": 1 }, - "entries": [ - { "item": 1512, "weight": 30 }, - { "item": 56, "weight": 35 }, - { "type": "empty", "weight": 35 } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/sub/Desk_military.json b/data/2016/lootTables/containers/sub/Desk_military.json deleted file mode 100644 index 7db1dfb488..0000000000 --- a/data/2016/lootTables/containers/sub/Desk_military.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { "min": 0, "max": 1 }, - "entries": [ - { "item": 1895, "weight": 20 }, - { "item": 1429, "weight": 20, "count": { "min": 1, "max": 3 } }, - { "item": 1428, "weight": 15, "count": { "min": 1, "max": 2 } }, - { "item": 1998, "weight": 15, "count": { "min": 1, "max": 2 } }, - { "type": "empty", "weight": 30 } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/sub/Desk_residential.json b/data/2016/lootTables/containers/sub/Desk_residential.json deleted file mode 100644 index 18ecbdbae5..0000000000 --- a/data/2016/lootTables/containers/sub/Desk_residential.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { "min": 0, "max": 1 }, - "entries": [ - { "item": 2273, "weight": 10 }, - { "item": 1696, "weight": 20 }, - { "item": 2170, "weight": 8 }, - { "item": 57, "weight": 8 }, - { "item": 56, "weight": 15 }, - { "type": "empty", "weight": 42 } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/sub/Dresser_clothing.json b/data/2016/lootTables/containers/sub/Dresser_clothing.json deleted file mode 100644 index 1ca8ef4489..0000000000 --- a/data/2016/lootTables/containers/sub/Dresser_clothing.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { "min": 0, "max": 1 }, - "entries": [ - { "item": 2218, "weight": 15 }, - { "item": 2209, "weight": 15 }, - { "item": 2206, "weight": 15 }, - { "item": 2088, "weight": 20 }, - { "item": 2177, "weight": 20 }, - { "type": "empty", "weight": 15 } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/sub/Dresser_residential.json b/data/2016/lootTables/containers/sub/Dresser_residential.json deleted file mode 100644 index ab5369457a..0000000000 --- a/data/2016/lootTables/containers/sub/Dresser_residential.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { "min": 0, "max": 1 }, - "entries": [ - { "item": 2107, "weight": 20 }, - { "item": 2211, "weight": 20 }, - { "item": 2110, "weight": 20 }, - { "item": 2206, "weight": 15 }, - { "type": "empty", "weight": 25 } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/sub/File_junk.json b/data/2016/lootTables/containers/sub/File_junk.json deleted file mode 100644 index 2579dcb7f5..0000000000 --- a/data/2016/lootTables/containers/sub/File_junk.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { "min": 0, "max": 1 }, - "entries": [ - { "item": 57, "weight": 15 }, - { "item": 56, "weight": 80 }, - { "item": 1436, "weight": 5 } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/sub/File_military.json b/data/2016/lootTables/containers/sub/File_military.json deleted file mode 100644 index d78165c061..0000000000 --- a/data/2016/lootTables/containers/sub/File_military.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { "min": 0, "max": 1 }, - "entries": [ - { - "item": 2229, - "weight": 5, - "functions": [{ "function": "set_damage", "min": 0.3, "max": 0.9 }] - }, - { "item": 2325, "weight": 20, "count": { "min": 1, "max": 3 } }, - { "item": 1428, "weight": 20, "count": { "min": 1, "max": 3 } }, - { "item": 1998, "weight": 20, "count": { "min": 1, "max": 3 } }, - { "item": 1429, "weight": 15, "count": { "min": 1, "max": 2 } }, - { "type": "empty", "weight": 20 } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/sub/File_rare.json b/data/2016/lootTables/containers/sub/File_rare.json deleted file mode 100644 index c359dbe9cf..0000000000 --- a/data/2016/lootTables/containers/sub/File_rare.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { "min": 0, "max": 1 }, - "entries": [ - { "item": 2645, "weight": 15 }, - { "item": 2646, "weight": 15 }, - { "item": 2647, "weight": 15 }, - { "item": 2648, "weight": 15 }, - { "type": "empty", "weight": 40 } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/sub/File_residential.json b/data/2016/lootTables/containers/sub/File_residential.json deleted file mode 100644 index cc66e239bb..0000000000 --- a/data/2016/lootTables/containers/sub/File_residential.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { "min": 0, "max": 1 }, - "entries": [ - { "item": 2645, "weight": 1 }, - { "item": 2646, "weight": 1 }, - { "item": 2647, "weight": 1 }, - { "item": 2648, "weight": 1 }, - { "item": 1436, "weight": 20 }, - { "type": "empty", "weight": 76 } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/sub/File_uncommon.json b/data/2016/lootTables/containers/sub/File_uncommon.json deleted file mode 100644 index f724795d66..0000000000 --- a/data/2016/lootTables/containers/sub/File_uncommon.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { "min": 0, "max": 1 }, - "entries": [ - { "item": 1992, "weight": 28, "count": { "min": 1, "max": 3 } }, - { "item": 1719, "weight": 22, "count": { "min": 1, "max": 2 } }, - { "item": 1998, "weight": 25, "count": { "min": 1, "max": 4 } }, - { "item": 1428, "weight": 25, "count": { "min": 1, "max": 4 } } - ] - } - ] -} diff --git a/data/2016/lootTables/containers/sub/Vehicles_common.json b/data/2016/lootTables/containers/sub/Vehicles_common.json deleted file mode 100644 index 4046561e9d..0000000000 --- a/data/2016/lootTables/containers/sub/Vehicles_common.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "type": "container", - "pools": [ - { - "conditions": [], - "rolls": { "min": 0, "max": 1 }, - "entries": [ - { "item": 1467, "weight": 15 }, - { "item": 48, "weight": 25, "count": { "min": 1, "max": 3 } }, - { "item": 46, "weight": 20, "count": { "min": 1, "max": 2 } }, - { "item": 155, "weight": 15 }, - { "type": "empty", "weight": 25 } - ] - } - ] -} diff --git a/data/2016/lootTables/ground/GasStation.json b/data/2016/lootTables/ground/GasStation.json deleted file mode 100644 index df0f65e9a5..0000000000 --- a/data/2016/lootTables/ground/GasStation.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "type": "ground", - "spawnChance": 100, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 1384, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 73, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawnerCommercial_Tier00.adr.json b/data/2016/lootTables/ground/ItemSpawnerCommercial_Tier00.adr.json deleted file mode 100644 index d4a067f15b..0000000000 --- a/data/2016/lootTables/ground/ItemSpawnerCommercial_Tier00.adr.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "type": "ground", - "spawnChance": 20, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 134, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1696, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1701, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1353, - "weight": 15, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1535, - "weight": 10, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 7, - "weight": 20, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2273, - "weight": 7, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 56, - "weight": 15, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 57, - "weight": 10, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2536, - "weight": 7, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawnerFarm.adr.json b/data/2016/lootTables/ground/ItemSpawnerFarm.adr.json deleted file mode 100644 index 06046f02c9..0000000000 --- a/data/2016/lootTables/ground/ItemSpawnerFarm.adr.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "type": "ground", - "spawnChance": 40, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 25, - "weight": 23, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 58, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1987, - "weight": 15, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1988, - "weight": 15, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 3, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1353, - "weight": 15, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawnerHospital.adr.json b/data/2016/lootTables/ground/ItemSpawnerHospital.adr.json deleted file mode 100644 index 6f9f7462e3..0000000000 --- a/data/2016/lootTables/ground/ItemSpawnerHospital.adr.json +++ /dev/null @@ -1,107 +0,0 @@ -{ - "type": "ground", - "spawnChance": 20, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 78, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1402, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 24, - "weight": 100, - "count": { - "min": 1, - "max": 2 - } - }, - { - "item": 2510, - "weight": 100, - "count": { - "min": 1, - "max": 2 - } - }, - { - "item": 1508, - "weight": 100, - "count": { - "min": 1, - "max": 2 - } - }, - { - "item": 2555, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2558, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2561, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1535, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1353, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1742, - "weight": 25, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 23, - "weight": 100, - "count": { - "min": 2, - "max": 5 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawnerIndustrial_Tier00.adr.json b/data/2016/lootTables/ground/ItemSpawnerIndustrial_Tier00.adr.json deleted file mode 100644 index f78fc395bb..0000000000 --- a/data/2016/lootTables/ground/ItemSpawnerIndustrial_Tier00.adr.json +++ /dev/null @@ -1,179 +0,0 @@ -{ - "type": "ground", - "spawnChance": 15, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 1696, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1701, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 9, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1730, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2595, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1728, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 90, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1731, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2727, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1729, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 134, - "weight": 20, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 82, - "weight": 25, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 142, - "weight": 20, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1353, - "weight": 15, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 109, - "weight": 50, - "count": { - "min": 1, - "max": 5 - } - }, - { - "item": 46, - "weight": 30, - "count": { - "min": 1, - "max": 3 - } - }, - { - "item": 48, - "weight": 40, - "count": { - "min": 1, - "max": 4 - } - }, - { - "item": 47, - "weight": 20, - "count": { - "min": 1, - "max": 2 - } - }, - { - "item": 58, - "weight": 20, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 155, - "weight": 40, - "count": { - "min": 1, - "max": 2 - } - }, - { - "item": 2273, - "weight": 7, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawnerRare_Tier00.adr.json b/data/2016/lootTables/ground/ItemSpawnerRare_Tier00.adr.json deleted file mode 100644 index c79f7c17ca..0000000000 --- a/data/2016/lootTables/ground/ItemSpawnerRare_Tier00.adr.json +++ /dev/null @@ -1,143 +0,0 @@ -{ - "type": "ground", - "spawnChance": 15, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 1428, - "weight": 120, - "count": { - "min": 1, - "max": 4 - } - }, - { - "item": 1998, - "weight": 120, - "count": { - "min": 1, - "max": 4 - } - }, - { - "item": 1992, - "weight": 120, - "count": { - "min": 1, - "max": 4 - } - }, - { - "item": 1719, - "weight": 80, - "count": { - "min": 1, - "max": 4 - } - }, - { - "item": 1429, - "weight": 50, - "count": { - "min": 1, - "max": 5 - } - }, - { - "item": 2325, - "weight": 50, - "count": { - "min": 1, - "max": 5 - } - }, - { - "item": 1469, - "weight": 50, - "count": { - "min": 1, - "max": 4 - } - }, - { - "item": 1511, - "weight": 50, - "count": { - "min": 1, - "max": 4 - } - }, - { - "item": 2, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1997, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1991, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1718, - "weight": 25, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1373, - "weight": 13, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1374, - "weight": 10, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 10, - "weight": 10, - "count": { - "min": 1, - "max": 1 - } - } - ] - }, - { - "conditions": [{ "condition": "poi_tag", "tags": ["military"] }], - "entries": [ - { - "item": 2229, - "weight": 80, - "count": { "min": 1, "max": 1 }, - "functions": [{ "function": "set_damage", "min": 0.2, "max": 0.8 }] - }, - { "type": "empty", "weight": 20 } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawnerResidential_Tier00.adr.json b/data/2016/lootTables/ground/ItemSpawnerResidential_Tier00.adr.json deleted file mode 100644 index 1a69bbc9aa..0000000000 --- a/data/2016/lootTables/ground/ItemSpawnerResidential_Tier00.adr.json +++ /dev/null @@ -1,275 +0,0 @@ -{ - "type": "ground", - "spawnChance": 20, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 1373, - "weight": 6, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1374, - "weight": 6, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2, - "weight": 7, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1997, - "weight": 7, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 134, - "weight": 20, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 142, - "weight": 20, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2088, - "weight": 40, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2177, - "weight": 40, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2217, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1696, - "weight": 10, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 84, - "weight": 15, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2102, - "weight": 15, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2162, - "weight": 15, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2170, - "weight": 15, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 7, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1467, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1353, - "weight": 25, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1368, - "weight": 15, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1428, - "weight": 15, - "count": { - "min": 1, - "max": 4 - } - }, - { - "item": 1998, - "weight": 15, - "count": { - "min": 1, - "max": 4 - } - }, - { - "item": 1992, - "weight": 15, - "count": { - "min": 1, - "max": 4 - } - }, - { - "item": 1719, - "weight": 11, - "count": { - "min": 1, - "max": 4 - } - }, - { - "item": 1429, - "weight": 3, - "count": { - "min": 1, - "max": 5 - } - }, - { - "item": 2325, - "weight": 3, - "count": { - "min": 1, - "max": 5 - } - }, - { - "item": 1469, - "weight": 3, - "count": { - "min": 1, - "max": 4 - } - }, - { - "item": 1511, - "weight": 3, - "count": { - "min": 1, - "max": 4 - } - }, - { - "item": 1701, - "weight": 10, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 78, - "weight": 15, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1542, - "weight": 20, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1724, - "weight": 10, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1721, - "weight": 10, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2038, - "weight": 8, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2273, - "weight": 7, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawnerWorld_Tier00.adr.json b/data/2016/lootTables/ground/ItemSpawnerWorld_Tier00.adr.json deleted file mode 100644 index 5d2343bcba..0000000000 --- a/data/2016/lootTables/ground/ItemSpawnerWorld_Tier00.adr.json +++ /dev/null @@ -1,141 +0,0 @@ -{ - "type": "ground", - "spawnChance": 20, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 83, - "weight": 30, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1353, - "weight": 15, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1368, - "weight": 10, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2088, - "weight": 40, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2177, - "weight": 40, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2217, - "weight": 20, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 3708, - "weight": 20, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2550, - "weight": 20, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 3, - "weight": 20, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2102, - "weight": 40, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2162, - "weight": 40, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2170, - "weight": 10, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 7, - "weight": 20, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2273, - "weight": 7, - "count": { - "min": 1, - "max": 1 - } - } - ] - }, - { - "conditions": [{ "condition": "poi_tag", "tags": ["high_tier"] }], - "entries": [ - { "item": 10, "weight": 5, "functions": [{ "function": "set_damage", "min": 0.3, "max": 0.9 }] }, - { "item": 1429, "weight": 10, "count": { "min": 1, "max": 2 } }, - { "type": "empty", "weight": 85 } - ] - }, - { - "conditions": [{ "condition": "poi_tag", "tags": ["military"] }], - "entries": [ - { "item": 2229, "weight": 3, "functions": [{ "function": "set_damage", "min": 0.3, "max": 0.9 }] }, - { "item": 2325, "weight": 8, "count": { "min": 1, "max": 2 } }, - { "item": 10, "weight": 5, "functions": [{ "function": "set_damage", "min": 0.2, "max": 0.8 }] }, - { "item": 1429, "weight": 10, "count": { "min": 1, "max": 2 } }, - { "type": "empty", "weight": 74 } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_AmmoBox02.adr.json b/data/2016/lootTables/ground/ItemSpawner_AmmoBox02.adr.json deleted file mode 100644 index c274cac542..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_AmmoBox02.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 20, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 1428, - "weight": 100, - "count": { - "min": 1, - "max": 4 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_AmmoBox02_12GaShotgun.adr.json b/data/2016/lootTables/ground/ItemSpawner_AmmoBox02_12GaShotgun.adr.json deleted file mode 100644 index 741547d7d5..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_AmmoBox02_12GaShotgun.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 40, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 1511, - "weight": 100, - "count": { - "min": 1, - "max": 4 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_AmmoBox02_1911.adr.json b/data/2016/lootTables/ground/ItemSpawner_AmmoBox02_1911.adr.json deleted file mode 100644 index d62d392b8c..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_AmmoBox02_1911.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 20, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 1428, - "weight": 100, - "count": { - "min": 1, - "max": 6 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_AmmoBox02_308Rifle.adr.json b/data/2016/lootTables/ground/ItemSpawner_AmmoBox02_308Rifle.adr.json deleted file mode 100644 index 4588d01ea4..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_AmmoBox02_308Rifle.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 40, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 1469, - "weight": 100, - "count": { - "min": 1, - "max": 4 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_AmmoBox02_M16A4.adr.json b/data/2016/lootTables/ground/ItemSpawner_AmmoBox02_M16A4.adr.json deleted file mode 100644 index 8683963edb..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_AmmoBox02_M16A4.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 40, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 1429, - "weight": 100, - "count": { - "min": 1, - "max": 4 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_BackpackOnGround001.adr.json b/data/2016/lootTables/ground/ItemSpawner_BackpackOnGround001.adr.json deleted file mode 100644 index 3dc9432b73..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_BackpackOnGround001.adr.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "type": "ground", - "spawnChance": 8, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 2038, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2124, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_CannedFood.adr.json b/data/2016/lootTables/ground/ItemSpawner_CannedFood.adr.json deleted file mode 100644 index 578e89e27f..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_CannedFood.adr.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "type": "ground", - "spawnChance": 20, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 56, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 7, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Clothes_BaseballCap.adr.json b/data/2016/lootTables/ground/ItemSpawner_Clothes_BaseballCap.adr.json deleted file mode 100644 index 85eec56572..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Clothes_BaseballCap.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 20, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 2102, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Clothes_Beanie.adr.json b/data/2016/lootTables/ground/ItemSpawner_Clothes_Beanie.adr.json deleted file mode 100644 index f18053af7a..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Clothes_Beanie.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 20, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 2162, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Clothes_FoldedShirt.adr.json b/data/2016/lootTables/ground/ItemSpawner_Clothes_FoldedShirt.adr.json deleted file mode 100644 index 7ccad25774..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Clothes_FoldedShirt.adr.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "type": "ground", - "spawnChance": 20, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 2088, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2177, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Clothes_Helmet_Tactical_ChipsScratches.adr.json b/data/2016/lootTables/ground/ItemSpawner_Clothes_Helmet_Tactical_ChipsScratches.adr.json deleted file mode 100644 index 6aa3a73438..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Clothes_Helmet_Tactical_ChipsScratches.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 10, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 2172, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Clothes_MotorcycleHelmet.adr.json b/data/2016/lootTables/ground/ItemSpawner_Clothes_MotorcycleHelmet.adr.json deleted file mode 100644 index 1a80efcfbf..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Clothes_MotorcycleHelmet.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 10, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 2170, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_FirstAidKit.adr.json b/data/2016/lootTables/ground/ItemSpawner_FirstAidKit.adr.json deleted file mode 100644 index 8ecd5db138..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_FirstAidKit.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 5, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 78, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_GasCan01.adr.json b/data/2016/lootTables/ground/ItemSpawner_GasCan01.adr.json deleted file mode 100644 index 8afa46fbde..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_GasCan01.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 25, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 73, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Log01.adr.json b/data/2016/lootTables/ground/ItemSpawner_Log01.adr.json deleted file mode 100644 index cbaf0ea024..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Log01.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 50, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 16, - "weight": 100, - "count": { - "min": 1, - "max": 4 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_WaterContainer_Small_Purified.adr.json b/data/2016/lootTables/ground/ItemSpawner_WaterContainer_Small_Purified.adr.json deleted file mode 100644 index 20fe17f54d..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_WaterContainer_Small_Purified.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 10, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 1371, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Weapon_45Auto.adr.json b/data/2016/lootTables/ground/ItemSpawner_Weapon_45Auto.adr.json deleted file mode 100644 index bfd225c6f1..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Weapon_45Auto.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 20, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 2, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Weapon_Bat01.adr.json b/data/2016/lootTables/ground/ItemSpawner_Weapon_Bat01.adr.json deleted file mode 100644 index dd61dbd3da..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Weapon_Bat01.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 10, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 1724, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Weapon_Bat02.adr.json b/data/2016/lootTables/ground/ItemSpawner_Weapon_Bat02.adr.json deleted file mode 100644 index 43df42437c..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Weapon_Bat02.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 10, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 1721, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Weapon_Bow.adr.json b/data/2016/lootTables/ground/ItemSpawner_Weapon_Bow.adr.json deleted file mode 100644 index 6e0638bb2c..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Weapon_Bow.adr.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "type": "ground", - "spawnChance": 20, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 113, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1986, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Weapon_ClawHammer01.adr.json b/data/2016/lootTables/ground/ItemSpawner_Weapon_ClawHammer01.adr.json deleted file mode 100644 index 37574826ed..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Weapon_ClawHammer01.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 20, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 1536, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Weapon_CombatKnife01.adr.json b/data/2016/lootTables/ground/ItemSpawner_Weapon_CombatKnife01.adr.json deleted file mode 100644 index 7b73de2bfd..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Weapon_CombatKnife01.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 20, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 84, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Weapon_Crowbar01.adr.json b/data/2016/lootTables/ground/ItemSpawner_Weapon_Crowbar01.adr.json deleted file mode 100644 index ad9ba79352..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Weapon_Crowbar01.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 35, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 82, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Weapon_FireAxe01.adr.json b/data/2016/lootTables/ground/ItemSpawner_Weapon_FireAxe01.adr.json deleted file mode 100644 index 0078d29793..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Weapon_FireAxe01.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 20, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 1745, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Weapon_GrenadeFlashbang.adr.json b/data/2016/lootTables/ground/ItemSpawner_Weapon_GrenadeFlashbang.adr.json deleted file mode 100644 index 131ffd4339..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Weapon_GrenadeFlashbang.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 20, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 2235, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Weapon_GrenadeGas.adr.json b/data/2016/lootTables/ground/ItemSpawner_Weapon_GrenadeGas.adr.json deleted file mode 100644 index ba085c5143..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Weapon_GrenadeGas.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 20, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 2237, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Weapon_GrenadeHE.adr.json b/data/2016/lootTables/ground/ItemSpawner_Weapon_GrenadeHE.adr.json deleted file mode 100644 index 4681f5a738..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Weapon_GrenadeHE.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 20, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 65, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Weapon_GrenadeSmoke.adr.json b/data/2016/lootTables/ground/ItemSpawner_Weapon_GrenadeSmoke.adr.json deleted file mode 100644 index 5d805f3d91..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Weapon_GrenadeSmoke.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 20, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 2236, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Weapon_Guitar01.adr.json b/data/2016/lootTables/ground/ItemSpawner_Weapon_Guitar01.adr.json deleted file mode 100644 index 9e2e603939..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Weapon_Guitar01.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 20, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 1733, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Weapon_Hatchet01.adr.json b/data/2016/lootTables/ground/ItemSpawner_Weapon_Hatchet01.adr.json deleted file mode 100644 index f82b1fd236..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Weapon_Hatchet01.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 20, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 3, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Weapon_M16A4.adr.json b/data/2016/lootTables/ground/ItemSpawner_Weapon_M16A4.adr.json deleted file mode 100644 index 71c9ff8b07..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Weapon_M16A4.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 40, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 10, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Weapon_M24.adr.json b/data/2016/lootTables/ground/ItemSpawner_Weapon_M24.adr.json deleted file mode 100644 index 867836a1f2..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Weapon_M24.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 50, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 1373, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Weapon_M9Auto.adr.json b/data/2016/lootTables/ground/ItemSpawner_Weapon_M9Auto.adr.json deleted file mode 100644 index dacd9c1758..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Weapon_M9Auto.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 20, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 1997, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Weapon_Machete01.adr.json b/data/2016/lootTables/ground/ItemSpawner_Weapon_Machete01.adr.json deleted file mode 100644 index eca5236ad3..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Weapon_Machete01.adr.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "type": "ground", - "spawnChance": 20, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 83, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2961, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Weapon_Pipe01.adr.json b/data/2016/lootTables/ground/ItemSpawner_Weapon_Pipe01.adr.json deleted file mode 100644 index c8f26b6ca2..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Weapon_Pipe01.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 20, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 1448, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Weapon_PumpShotgun01.adr.json b/data/2016/lootTables/ground/ItemSpawner_Weapon_PumpShotgun01.adr.json deleted file mode 100644 index 484b3b2af4..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Weapon_PumpShotgun01.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 40, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 1374, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Weapon_WoodAxe01.adr.json b/data/2016/lootTables/ground/ItemSpawner_Weapon_WoodAxe01.adr.json deleted file mode 100644 index f9bedbd3a4..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Weapon_WoodAxe01.adr.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "type": "ground", - "spawnChance": 20, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 58, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Z1_MilitaryBase_Hangar.adr.json b/data/2016/lootTables/ground/ItemSpawner_Z1_MilitaryBase_Hangar.adr.json deleted file mode 100644 index 65d2a9c82e..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Z1_MilitaryBase_Hangar.adr.json +++ /dev/null @@ -1,187 +0,0 @@ -{ - "type": "ground", - "spawnChance": 20, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 1696, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1701, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 9, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1730, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2595, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1728, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 90, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1731, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2727, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1729, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 134, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 46, - "weight": 100, - "count": { - "min": 1, - "max": 3 - } - }, - { - "item": 48, - "weight": 100, - "count": { - "min": 1, - "max": 4 - } - }, - { - "item": 1448, - "weight": 100, - "count": { - "min": 1, - "max": 2 - } - }, - { - "item": 82, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1536, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 73, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 74, - "weight": 50, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1538, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2236, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2235, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 14, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Z1_MilitaryBase_MotorPool.adr.json b/data/2016/lootTables/ground/ItemSpawner_Z1_MilitaryBase_MotorPool.adr.json deleted file mode 100644 index 5256b39c45..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Z1_MilitaryBase_MotorPool.adr.json +++ /dev/null @@ -1,147 +0,0 @@ -{ - "type": "ground", - "spawnChance": 50, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 134, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1542, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 84, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1804, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 48, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 74, - "weight": 50, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1380, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 155, - "weight": 100, - "count": { - "min": 1, - "max": 2 - } - }, - { - "item": 2652, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2659, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2655, - "weight": 5, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1402, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2124, - "weight": 10, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2273, - "weight": 50, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2236, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2235, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 14, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Z1_MilitaryBase_Tents1.adr.json b/data/2016/lootTables/ground/ItemSpawner_Z1_MilitaryBase_Tents1.adr.json deleted file mode 100644 index 67f1a6cafb..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Z1_MilitaryBase_Tents1.adr.json +++ /dev/null @@ -1,227 +0,0 @@ -{ - "type": "ground", - "spawnChance": 50, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 2246, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1991, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2652, - "weight": 10, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2659, - "weight": 10, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2655, - "weight": 10, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 92, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2170, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2172, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2148, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 78, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1428, - "weight": 100, - "count": { - "min": 1, - "max": 5 - } - }, - { - "item": 1998, - "weight": 100, - "count": { - "min": 1, - "max": 5 - } - }, - { - "item": 1992, - "weight": 100, - "count": { - "min": 1, - "max": 5 - } - }, - { - "item": 1719, - "weight": 100, - "count": { - "min": 1, - "max": 5 - } - }, - { - "item": 1429, - "weight": 100, - "count": { - "min": 1, - "max": 5 - } - }, - { - "item": 2325, - "weight": 100, - "count": { - "min": 1, - "max": 5 - } - }, - { - "item": 1469, - "weight": 100, - "count": { - "min": 1, - "max": 4 - } - }, - { - "item": 1511, - "weight": 100, - "count": { - "min": 1, - "max": 4 - } - }, - { - "item": 1700, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1402, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2124, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2273, - "weight": 50, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2236, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2235, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1373, - "weight": 50, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1374, - "weight": 50, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 14, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/lootTables/ground/ItemSpawner_Z1_MilitaryBase_Tents2.adr.json b/data/2016/lootTables/ground/ItemSpawner_Z1_MilitaryBase_Tents2.adr.json deleted file mode 100644 index 013da9fc13..0000000000 --- a/data/2016/lootTables/ground/ItemSpawner_Z1_MilitaryBase_Tents2.adr.json +++ /dev/null @@ -1,123 +0,0 @@ -{ - "type": "ground", - "spawnChance": 10, - "pools": [ - { - "conditions": [], - "entries": [ - { - "item": 14, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1718, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 1469, - "weight": 100, - "count": { - "min": 1, - "max": 4 - } - }, - { - "item": 1511, - "weight": 100, - "count": { - "min": 1, - "max": 4 - } - }, - { - "item": 11, - "weight": 120, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 74, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2271, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2273, - "weight": 50, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2236, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2652, - "weight": 10, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2659, - "weight": 10, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2655, - "weight": 10, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 2235, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - }, - { - "item": 14, - "weight": 100, - "count": { - "min": 1, - "max": 1 - } - } - ] - } - ] -} \ No newline at end of file diff --git a/data/2016/sampleData/defaultconfig.yaml b/data/2016/sampleData/defaultconfig.yaml index 9cc72259f0..a1e893f774 100644 --- a/data/2016/sampleData/defaultconfig.yaml +++ b/data/2016/sampleData/defaultconfig.yaml @@ -18,7 +18,6 @@ server: damageWeapons: true # Damage weapons on firing disablePOIManager: false # Disables the POI messages disableMapBoundsCheck: false # Disables inboundscheck - disableBaseCheck: false # Disables in-base permission checks (hide/teleport/kill) # Rcon @@ -103,9 +102,6 @@ worldobjects: lootDespawnTimer: 2400000 # 40 minutes deadNpcDespawnTimer: 600000 # 10 minutes lootbagDespawnTimer: 1800000 # 30 minutes - maxNpcDespawnsPerRun: 40 - maxLootbagDespawnsPerRun: 40 - maxItemDespawnsPerRun: 120 # Misc vehicleSpawnCap: 120 @@ -124,10 +120,6 @@ worldobjects: crowbarHitRewardChance: 20 # 100 max crowbarHitDamage: 10 # Default 25 - # Scrap limit for each grid cell - gridScrapLimit: 50 - gridScrapLimitEnabled: true - # Trees, blackberry bushes, and sticks speedtree: minBlackberryHarvest: 1 diff --git a/data/2016/zoneData/Z1_POIs.json b/data/2016/zoneData/Z1_POIs.json index 5935b9fd39..3fc7dc138e 100644 --- a/data/2016/zoneData/Z1_POIs.json +++ b/data/2016/zoneData/Z1_POIs.json @@ -2,7 +2,6 @@ { "POIid": 1, "POIname": "Pleasant Valley", - "tags": ["residential", "city", "high_tier"], "stringId": 9570, "range": 500, "bounds": [ @@ -66,7 +65,6 @@ { "POIid": 3, "POIname": "Bubba's Truck Stop", - "tags": ["commercial", "truck_stop", "medium_tier"], "stringId": 9550, "range": 250, "bounds": [ @@ -87,7 +85,6 @@ { "POIid": 4, "POIname": "Bumjick Farms", - "tags": ["rural", "farm", "medium_tier"], "stringId": 9552, "range": 200, "bounds": [ @@ -123,7 +120,6 @@ { "POIid": 5, "POIname": "Cranberry", - "tags": ["residential", "city", "high_tier"], "stringId": 9554, "range": 300, "bounds": [ @@ -156,7 +152,6 @@ { "POIid": 6, "POIname": "De Soto Service Stop", - "tags": ["commercial", "truck_stop"], "stringId": 9556, "range": 150, "bounds": [ @@ -177,7 +172,6 @@ { "POIid": 7, "POIname": "Hemingway Truck Stop", - "tags": ["commercial", "truck_stop"], "stringId": 9558, "range": 100, "bounds": [ @@ -198,7 +192,6 @@ { "POIid": 8, "POIname": "Hunter's Truck Stop", - "tags": ["commercial", "truck_stop"], "stringId": 9560, "range": 150, "bounds": [ @@ -226,7 +219,6 @@ { "POIid": 9, "POIname": "KZPV 98.5 FM Radio", - "tags": ["commercial"], "stringId": 9562, "range": 100, "position": [ @@ -239,7 +231,6 @@ { "POIid": 10, "POIname": "Lone Pine Development", - "tags": ["residential", "medium_tier"], "stringId": 9564, "range": 300, "bounds": [ @@ -260,7 +251,6 @@ { "POIid": 11, "POIname": "Misty Peak Dam", - "tags": ["industrial", "medium_tier"], "stringId": 9566, "range": 150, "bounds": [ @@ -281,7 +271,6 @@ { "POIid": 12, "POIname": "Opfer Wilderness Camp", - "tags": ["wilderness", "rural"], "stringId": 9568, "range": 300, "position": [ @@ -294,7 +283,6 @@ { "POIid": 13, "POIname": "Ranchito Taquito", - "tags": ["commercial", "city", "medium_tier"], "stringId": 9572, "range": 400, "bounds": [ @@ -388,7 +376,6 @@ { "POIid": 14, "POIname": "The Villas Development", - "tags": ["residential", "medium_tier"], "stringId": 9576, "range": 200, "bounds": [ @@ -409,7 +396,6 @@ { "POIid": 15, "POIname": "Wake Hills Hamlet", - "tags": ["residential", "rural"], "stringId": 9578, "range": 100, "position": [ @@ -422,7 +408,6 @@ { "POIid": 16, "POIname": "Runamok Campsite", - "tags": ["wilderness", "rural"], "stringId": 9574, "range": 50, "position": [ @@ -435,7 +420,6 @@ { "POIid": 17, "POIname": "Kurama Medical Facility", - "tags": ["medical", "high_tier"], "stringId": 12838, "range": 350, "bounds": [ @@ -462,7 +446,6 @@ { "POIid": 18, "POIname": "Military Base", - "tags": ["military", "high_tier"], "stringId": 9550, "range": 0, "bounds": [ @@ -483,7 +466,6 @@ { "POIid": 19, "POIname": "Toxic", - "tags": ["toxic", "medium_tier"], "stringId": 9550, "range": 0, "bounds": [ @@ -508,7 +490,6 @@ { "POIid": 20, "POIname": "Governor's Compound", - "tags": ["military", "high_tier"], "stringId": 13566, "range": 200, "bounds": [ diff --git a/docker/2016/zoneServer.js b/docker/2016/zoneServer.js index 7ce641468e..067300bba4 100644 --- a/docker/2016/zoneServer.js +++ b/docker/2016/zoneServer.js @@ -1,12 +1,3 @@ -if (process.env.APM_ENABLED == "true") { - require("elastic-apm-node").start({ - serviceName: process.env.ELASTIC_APM_SERVICE_NAME, - secretToken: process.env.ELASTIC_APM_SECRET_TOKEN, - serverUrl: process.env.ELASTIC_APM_SERVER_URL, - environment: process.env.ELASTIC_APM_ENVIRONMENT, - }); -} - const { ZoneServer2016 } = require("../../h1z1-server"); const Zone = new ZoneServer2016( diff --git a/package-lock.json b/package-lock.json index f156d6c346..ba8a0b1484 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "h1z1-server", - "version": "0.48.1-0", + "version": "0.47.4-2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "h1z1-server", - "version": "0.48.1-0", + "version": "0.47.4-2", "hasInstallScript": true, "license": "GPL-3.0-only", "dependencies": { @@ -14,7 +14,6 @@ "@types/node": "25.2.0", "@types/ws": "8.18.1", "debug": "4.4.3", - "elastic-apm-node": "^4.15.0", "h1emu-core": "1.3.2", "h1z1-dataschema": "1.9.2", "js-yaml": "4.1.1", @@ -40,27 +39,6 @@ "node": ">=0.24.0 <26" } }, - "node_modules/@elastic/ecs-helpers": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@elastic/ecs-helpers/-/ecs-helpers-2.1.1.tgz", - "integrity": "sha512-ItoNazMnYdlUCmkBYTXc3SG6PF7UlVTbvMdHPvXkfTMPdwGv2G1Xtp5CjDHaGHGOZSwaDrW4RSCXvA/lMSU+rg==", - "license": "Apache-2.0", - "engines": { - "node": ">=10" - } - }, - "node_modules/@elastic/ecs-pino-format": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@elastic/ecs-pino-format/-/ecs-pino-format-1.5.0.tgz", - "integrity": "sha512-7MMVmT50ucEl7no8mUgCIl+pffBVNRl36uZi0vmalWa2xPWISBxM9k9WSP/WTgOkmGj9G35e5g3UfCS1zxshBg==", - "license": "Apache-2.0", - "dependencies": { - "@elastic/ecs-helpers": "^2.1.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@epic-web/invariant": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz", @@ -533,71 +511,6 @@ "sparse-bitfield": "^3.0.3" } }, - "node_modules/@opentelemetry/api": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", - "license": "Apache-2.0", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@opentelemetry/core": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", - "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/semantic-conventions": "1.28.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/resources": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", - "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.30.1", - "@opentelemetry/semantic-conventions": "1.28.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/sdk-metrics": { - "version": "1.30.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.30.1.tgz", - "integrity": "sha512-q9zcZ0Okl8jRgmy7eNW3Ku1XSgg3sDLa5evHZpCwjspw7E8Is4K/haRPDJrBcX3YSn/Y7gUvFnByNYEKQNbNog==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/core": "1.30.1", - "@opentelemetry/resources": "1.30.1" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "@opentelemetry/api": ">=1.3.0 <1.10.0" - } - }, - "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.28.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", - "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, "node_modules/@oxlint/darwin-arm64": { "version": "1.43.0", "resolved": "https://registry.npmjs.org/@oxlint/darwin-arm64/-/darwin-arm64-1.43.0.tgz", @@ -839,86 +752,11 @@ "@types/node": "*" } }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "license": "MIT", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-import-attributes": { - "version": "1.9.5", - "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", - "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", - "license": "MIT", - "peerDependencies": { - "acorn": "^8" - } - }, - "node_modules/after-all-results": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/after-all-results/-/after-all-results-2.0.0.tgz", - "integrity": "sha512-2zHEyuhSJOuCrmas9YV0YL/MFCWLxe1dS6k/ENhgYrb/JqyMnadLN4iIAc9kkZrbElMDyyAGH/0J18OPErOWLg==", - "license": "MIT" - }, - "node_modules/agentkeepalive": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", - "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", - "license": "MIT", - "dependencies": { - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, - "node_modules/async-value": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/async-value/-/async-value-1.2.2.tgz", - "integrity": "sha512-8rwtYe32OAS1W9CTwvknoyts+mc3ta8N7Pi0h7AjkMaKvsFbr39K+gEfZ7Z81aPXQ1sK5M23lgLy1QfZpcpadQ==", - "license": "MIT" - }, - "node_modules/async-value-promise": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/async-value-promise/-/async-value-promise-1.1.1.tgz", - "integrity": "sha512-c2RFDKjJle1rHa0YxN9Ysu97/QBu3Wa+NOejJxsX+1qVDJrkD3JL/GN1B3gaILAEXJXbu/4Z1lcoCHFESe/APA==", - "license": "MIT", - "dependencies": { - "async-value": "^1.2.2" - } - }, - "node_modules/atomic-sleep": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", - "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -926,53 +764,6 @@ "dev": true, "license": "MIT" }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.1.2" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/bignumber.js": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", - "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/binary-search": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz", - "integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA==", - "license": "CC0-1.0" - }, "node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", @@ -983,15 +774,6 @@ "balanced-match": "^1.0.0" } }, - "node_modules/breadth-filter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/breadth-filter/-/breadth-filter-2.0.0.tgz", - "integrity": "sha512-thQShDXnFWSk2oVBixRCyrWsFoV5tfOpWKHmxwafHQDNxCfDBk539utpvytNjmlFrTMqz41poLwJvA1MW3z0MQ==", - "license": "MIT", - "dependencies": { - "object.entries": "^1.0.4" - } - }, "node_modules/bson": { "version": "6.10.4", "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.4.tgz", @@ -1001,77 +783,6 @@ "node": ">=16.20.1" } }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1080,33 +791,6 @@ "node": ">=6" } }, - "node_modules/cjs-module-lexer": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", - "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", - "license": "MIT" - }, - "node_modules/console-log-level": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/console-log-level/-/console-log-level-1.4.1.tgz", - "integrity": "sha512-VZzbIORbP+PPcN/gg3DXClTLPLg5Slwd5fL2MIc+o1qZ4BXBvWyc6QxPk6T/Mkr6IVjRpoAGf32XxP3ZWMVRcQ==", - "license": "MIT" - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT" - }, "node_modules/cross-env": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz", @@ -1157,111 +841,6 @@ } } }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/elastic-apm-node": { - "version": "4.15.0", - "resolved": "https://registry.npmjs.org/elastic-apm-node/-/elastic-apm-node-4.15.0.tgz", - "integrity": "sha512-8N176AlRizpX+JcNP3xPPbl9HjiDG6PRTIY0XfY2SSmI7dAvxlYk/TFBcOXeGpoMTYCHZHNPUUL0ryvaMV7EGA==", - "license": "BSD-2-Clause", - "dependencies": { - "@elastic/ecs-pino-format": "^1.5.0", - "@opentelemetry/api": "^1.4.1", - "@opentelemetry/core": "^1.11.0", - "@opentelemetry/sdk-metrics": "^1.12.0", - "after-all-results": "^2.0.0", - "agentkeepalive": "^4.2.1", - "async-value-promise": "^1.1.1", - "basic-auth": "^2.0.1", - "breadth-filter": "^2.0.0", - "cookie": "^0.7.1", - "core-util-is": "^1.0.2", - "end-of-stream": "^1.4.4", - "error-callsites": "^2.0.4", - "error-stack-parser": "^2.0.6", - "escape-string-regexp": "^4.0.0", - "fast-safe-stringify": "^2.0.7", - "fast-stream-to-buffer": "^1.0.0", - "http-headers": "^3.0.2", - "import-in-the-middle": "1.14.4", - "json-bigint": "^1.0.0", - "lru-cache": "10.2.0", - "measured-reporting": "^1.51.1", - "module-details-from-path": "^1.0.3", - "monitor-event-loop-delay": "^1.0.0", - "object-filter-sequence": "^1.0.0", - "object-identity-map": "^1.0.2", - "original-url": "^1.2.3", - "pino": "^8.15.0", - "readable-stream": "^3.6.2", - "relative-microtime": "^2.0.0", - "require-in-the-middle": "^8.0.0", - "semver": "^7.5.4", - "shallow-clone-shim": "^2.0.0", - "source-map": "^0.8.0-beta.0", - "sql-summary": "^1.0.1", - "stream-chopper": "^3.0.1", - "unicode-byte-truncate": "^1.0.0" - }, - "engines": { - "node": ">=14.17.0" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -1275,54 +854,6 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/error-callsites": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/error-callsites/-/error-callsites-2.0.4.tgz", - "integrity": "sha512-V877Ch4FC4FN178fDK1fsrHN4I1YQIBdtjKrHh3BUHMnh3SMvwUVrqkaOgDpUuevgSNna0RBq6Ox9SGlxYrigA==", - "license": "MIT", - "engines": { - "node": ">=6.x" - } - }, - "node_modules/error-stack-parser": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", - "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", - "license": "MIT", - "dependencies": { - "stackframe": "^1.3.4" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/esbuild": { "version": "0.27.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", @@ -1365,18 +896,6 @@ "@esbuild/win32-x64": "0.27.2" } }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/esm": { "version": "3.2.25", "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", @@ -1386,54 +905,6 @@ "node": ">=6" } }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/fast-redact": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", - "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", - "license": "MIT" - }, - "node_modules/fast-stream-to-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fast-stream-to-buffer/-/fast-stream-to-buffer-1.0.0.tgz", - "integrity": "sha512-bI/544WUQlD2iXBibQbOMSmG07Hay7YrpXlKaeGTPT7H7pC0eitt3usak5vUwEvCGK/O7rUAM3iyQValGU22TQ==", - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.4.1" - } - }, - "node_modules/forwarded-parse": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/forwarded-parse/-/forwarded-parse-2.1.2.tgz", - "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==", - "license": "MIT" - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -1449,52 +920,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/get-tsconfig": { "version": "4.8.1", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", @@ -1521,18 +946,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/h1emu-core": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/h1emu-core/-/h1emu-core-1.3.2.tgz", @@ -1583,119 +996,6 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "license": "MIT" }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/http-headers": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/http-headers/-/http-headers-3.0.2.tgz", - "integrity": "sha512-87E1I+2Wg4dxxz4rcxElo3dxO/w1ZtgL1yA0Sb6vH3qU16vRKq1NjWQv9SCY3ly2OQROcoxHZOUpmelS+k6wOw==", - "license": "MIT", - "dependencies": { - "next-line": "^1.1.0" - } - }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.0.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/import-in-the-middle": { - "version": "1.14.4", - "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.14.4.tgz", - "integrity": "sha512-eWjxh735SJLFJJDs5X82JQ2405OdJeAHDBnaoFCfdr5GVc7AWc9xU7KbrF+3Xd5F2ccP1aQFKtY+65X6EfKZ7A==", - "license": "Apache-2.0", - "dependencies": { - "acorn": "^8.14.0", - "acorn-import-attributes": "^1.9.5", - "cjs-module-lexer": "^1.2.2", - "module-details-from-path": "^1.0.3" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/is-finite": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", - "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-integer": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-integer/-/is-integer-1.0.7.tgz", - "integrity": "sha512-RPQc/s9yBHSvpi+hs9dYiJ2cuFeU6x3TyyIp8O2H6SKEltIvJOzRj9ToyvcStDvPR/pS4rxgr1oBFajQjZ2Szg==", - "license": "WTFPL OR ISC", - "dependencies": { - "is-finite": "^1.0.0" - } - }, "node_modules/is-observable": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-observable/-/is-observable-2.1.0.tgz", @@ -1726,15 +1026,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/json-bigint": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", - "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", - "license": "MIT", - "dependencies": { - "bignumber.js": "^9.0.0" - } - }, "node_modules/linkify-it": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", @@ -1745,33 +1036,12 @@ "uc.micro": "^2.0.0" } }, - "node_modules/lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", - "license": "ISC", - "engines": { - "node": "14 || >=16.14" - } - }, "node_modules/lunr": { "version": "2.3.9", "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", "dev": true }, - "node_modules/mapcap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mapcap/-/mapcap-1.0.0.tgz", - "integrity": "sha512-KcNlZSlFPx+r1jYZmxEbTVymG+dIctf10WmWkuhrhrblM+KMoF77HelwihL5cxYlORye79KoR4IlOOk99lUJ0g==", - "license": "MIT" - }, "node_modules/markdown-it": { "version": "14.1.0", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", @@ -1790,15 +1060,6 @@ "markdown-it": "bin/markdown-it.mjs" } }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, "node_modules/mdurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", @@ -1806,34 +1067,6 @@ "dev": true, "license": "MIT" }, - "node_modules/measured-core": { - "version": "1.51.1", - "resolved": "https://registry.npmjs.org/measured-core/-/measured-core-1.51.1.tgz", - "integrity": "sha512-DZQP9SEwdqqYRvT2slMK81D/7xwdxXosZZBtLVfPSo6y5P672FBTbzHVdN4IQyUkUpcVOR9pIvtUy5Ryl7NKyg==", - "license": "MIT", - "dependencies": { - "binary-search": "^1.3.3", - "optional-js": "^2.0.0" - }, - "engines": { - "node": ">= 5.12" - } - }, - "node_modules/measured-reporting": { - "version": "1.51.1", - "resolved": "https://registry.npmjs.org/measured-reporting/-/measured-reporting-1.51.1.tgz", - "integrity": "sha512-JCt+2u6XT1I5lG3SuYqywE0e62DJuAzBcfMzWGUhIYtPQV2Vm4HiYt/durqmzsAbZV181CEs+o/jMKWJKkYIWw==", - "license": "MIT", - "dependencies": { - "console-log-level": "^1.4.1", - "mapcap": "^1.0.0", - "measured-core": "^1.51.1", - "optional-js": "^2.0.0" - }, - "engines": { - "node": ">= 5.12" - } - }, "node_modules/memory-pager": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", @@ -1856,12 +1089,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/module-details-from-path": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", - "integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==", - "license": "MIT" - }, "node_modules/mongodb": { "version": "6.20.0", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.20.0.tgz", @@ -1918,101 +1145,17 @@ "whatwg-url": "^14.1.0 || ^13.0.0" } }, - "node_modules/monitor-event-loop-delay": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/monitor-event-loop-delay/-/monitor-event-loop-delay-1.0.0.tgz", - "integrity": "sha512-YRIr1exCIfBDLZle8WHOfSo7Xg3M+phcZfq9Fx1L6Abo+atGp7cge5pM7PjyBn4s1oZI/BRD4EMrzQBbPpVb5Q==", - "license": "MIT" - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, - "node_modules/next-line": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-line/-/next-line-1.1.0.tgz", - "integrity": "sha512-+I10J3wKNoKddNxn0CNpoZ3eTZuqxjNM3b1GImVx22+ePI+Y15P8g/j3WsbP0fhzzrFzrtjOAoq5NCCucswXOQ==", - "license": "MIT" - }, - "node_modules/object-filter-sequence": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/object-filter-sequence/-/object-filter-sequence-1.0.0.tgz", - "integrity": "sha512-CsubGNxhIEChNY4cXYuA6KXafztzHqzLLZ/y3Kasf3A+sa3lL9thq3z+7o0pZqzEinjXT6lXDPAfVWI59dUyzQ==", - "license": "MIT" - }, - "node_modules/object-identity-map": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/object-identity-map/-/object-identity-map-1.0.2.tgz", - "integrity": "sha512-a2XZDGyYTngvGS67kWnqVdpoaJWsY7C1GhPJvejWAFCsUioTAaiTu8oBad7c6cI4McZxr4CmvnZeycK05iav5A==", - "license": "MIT", - "dependencies": { - "object.entries": "^1.1.0" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.entries": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", - "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/observable-fns": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/observable-fns/-/observable-fns-0.6.1.tgz", "integrity": "sha512-9gRK4+sRWzeN6AOewNBTLXir7Zl/i3GB6Yl26gK4flxz8BXVpD3kt8amREmWNb0mxYOGDotvE5a4N+PtGGKdkg==" }, - "node_modules/on-exit-leak-free": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", - "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/optional-js": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/optional-js/-/optional-js-2.3.0.tgz", - "integrity": "sha512-B0LLi+Vg+eko++0z/b8zIv57kp7HKEzaPJo7LowJXMUKYdf+3XJGu/cw03h/JhIOsLnP+cG5QnTHAuicjA5fMw==", - "license": "MIT" - }, - "node_modules/original-url": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/original-url/-/original-url-1.2.3.tgz", - "integrity": "sha512-BYm+pKYLtS4mVe/mgT3YKGtWV5HzN/XKiaIu1aK4rsxyjuHeTW9N+xVBEpJcY1onB3nccfH0RbzUEoimMqFUHQ==", - "license": "MIT", - "dependencies": { - "forwarded-parse": "^2.1.0" - } - }, "node_modules/oxlint": { "version": "1.43.0", "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.43.0.tgz", @@ -2057,60 +1200,6 @@ "node": ">=8" } }, - "node_modules/pino": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-8.21.0.tgz", - "integrity": "sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==", - "license": "MIT", - "dependencies": { - "atomic-sleep": "^1.0.0", - "fast-redact": "^3.1.1", - "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "^1.2.0", - "pino-std-serializers": "^6.0.0", - "process-warning": "^3.0.0", - "quick-format-unescaped": "^4.0.3", - "real-require": "^0.2.0", - "safe-stable-stringify": "^2.3.1", - "sonic-boom": "^3.7.0", - "thread-stream": "^2.6.0" - }, - "bin": { - "pino": "bin.js" - } - }, - "node_modules/pino-abstract-transport": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz", - "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==", - "license": "MIT", - "dependencies": { - "readable-stream": "^4.0.0", - "split2": "^4.0.0" - } - }, - "node_modules/pino-abstract-transport/node_modules/readable-stream": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", - "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/pino-std-serializers": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz", - "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==", - "license": "MIT" - }, "node_modules/prettier": { "version": "3.8.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", @@ -2127,21 +1216,6 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/process-warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", - "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==", - "license": "MIT" - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -2161,35 +1235,6 @@ "node": ">=6" } }, - "node_modules/quick-format-unescaped": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", - "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", - "license": "MIT" - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/real-require": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", - "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", - "license": "MIT", - "engines": { - "node": ">= 12.13.0" - } - }, "node_modules/recast-navigation": { "version": "0.43.0", "resolved": "https://registry.npmjs.org/recast-navigation/-/recast-navigation-0.43.0.tgz", @@ -2200,25 +1245,6 @@ "@recast-navigation/generators": "0.43.0" } }, - "node_modules/relative-microtime": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/relative-microtime/-/relative-microtime-2.0.0.tgz", - "integrity": "sha512-l18ha6HEZc+No/uK4GyAnNxgKW7nvEe35IaeN54sShMojtqik2a6GbTyuiezkjpPaqP874Z3lW5ysBo5irz4NA==", - "license": "MIT" - }, - "node_modules/require-in-the-middle": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-8.0.1.tgz", - "integrity": "sha512-QT7FVMXfWOYFbeRBF6nu+I6tr2Tf3u0q8RIEjNob/heKY/nh7drD/k7eeMFmSQgnTtCzLDcCu/XEnpW2wk4xCQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "module-details-from-path": "^1.0.3" - }, - "engines": { - "node": ">=9.3.0 || >=8.10.0 <9.0.0" - } - }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", @@ -2229,56 +1255,6 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/safe-stable-stringify": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", - "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/shallow-clone-shim": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shallow-clone-shim/-/shallow-clone-shim-2.0.0.tgz", - "integrity": "sha512-YRNymdiL3KGOoS67d73TEmk4tdPTO9GSMCoiphQsTcC9EtC+AOmMPjkyBkRoCJfW9ASsaZw1craaiw1dPN2D3Q==", - "license": "MIT" - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -2302,54 +1278,6 @@ "node": ">=8" } }, - "node_modules/sonic-boom": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.1.tgz", - "integrity": "sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==", - "license": "MIT", - "dependencies": { - "atomic-sleep": "^1.0.0" - } - }, - "node_modules/source-map": { - "version": "0.8.0-beta.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", - "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", - "deprecated": "The work that was done in this beta branch won't be included in future versions", - "license": "BSD-3-Clause", - "dependencies": { - "whatwg-url": "^7.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/source-map/node_modules/tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", - "license": "MIT", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/source-map/node_modules/webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "license": "BSD-2-Clause" - }, - "node_modules/source-map/node_modules/whatwg-url": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", - "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", - "license": "MIT", - "dependencies": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - }, "node_modules/sparse-bitfield": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", @@ -2359,74 +1287,6 @@ "memory-pager": "^1.0.2" } }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "license": "ISC", - "engines": { - "node": ">= 10.x" - } - }, - "node_modules/sql-summary": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sql-summary/-/sql-summary-1.0.1.tgz", - "integrity": "sha512-IpCr2tpnNkP3Jera4ncexsZUp0enJBLr+pHCyTweMUBrbJsTgQeLWx1FXLhoBj/MvcnUQpkgOn2EY8FKOkUzww==", - "license": "MIT" - }, - "node_modules/stackframe": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", - "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", - "license": "MIT" - }, - "node_modules/stream-chopper": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/stream-chopper/-/stream-chopper-3.0.1.tgz", - "integrity": "sha512-f7h+ly8baAE26iIjcp3VbnBkbIRGtrvV0X0xxFM/d7fwLTYnLzDPTXRKNxa2HZzohOrc96NTrR+FaV3mzOelNA==", - "license": "MIT", - "dependencies": { - "readable-stream": "^3.0.6" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/thread-stream": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.7.0.tgz", - "integrity": "sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==", - "license": "MIT", - "dependencies": { - "real-require": "^0.2.0" - } - }, "node_modules/threads": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/threads/-/threads-1.7.0.tgz", @@ -2535,28 +1395,6 @@ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "license": "MIT" }, - "node_modules/unicode-byte-truncate": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unicode-byte-truncate/-/unicode-byte-truncate-1.0.0.tgz", - "integrity": "sha512-GQgHk6DodEoKddKQdjnv7xKS9G09XCfHWX0R4RKht+EbUMSiVEmtWHGFO8HUm+6NvWik3E2/DG4MxTitOLL64A==", - "license": "MIT", - "dependencies": { - "is-integer": "^1.0.6", - "unicode-substring": "^0.1.0" - } - }, - "node_modules/unicode-substring": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/unicode-substring/-/unicode-substring-0.1.0.tgz", - "integrity": "sha512-36Xaw9wXi7MB/3/EQZZHkZyyiRNa9i3k9YtPAz2KfqMVH2xutdXyMHn4Igarmnvr+wOrfWa/6njhY+jPpXN2EQ==", - "license": "MIT" - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", @@ -2595,12 +1433,6 @@ "node": ">= 8" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, "node_modules/ws": { "version": "8.19.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", diff --git a/package.json b/package.json index 3488da772d..723e54d9a0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "h1z1-server", - "version": "0.48.1-0", + "version": "0.47.4-2", "description": "Library for emulating h1z1 servers", "author": "Quentin Gruber (http://github.com/quentingruber)", "license": "GPL-3.0-only", @@ -17,7 +17,6 @@ "@types/node": "25.2.0", "@types/ws": "8.18.1", "debug": "4.4.3", - "elastic-apm-node": "^4.15.0", "h1emu-core": "1.3.2", "h1z1-dataschema": "1.9.2", "js-yaml": "4.1.1", @@ -48,9 +47,6 @@ "start": "node --no-warnings --experimental-require-module ./scripts/h1z1-server-demo-2016.js", "start-dev": "npm run build && npm start", "start-echo": "npm run build && npm run build-benchs && node --inspect ./benchmarks/out/echo/echo-server-start.js", - "stress": "npm run build && npm run build-benchs && node benchmarks/out/zoneStress/zone-stress.js", - "stress-profile": "npm run build && npm run build-benchs && node --cpu-prof benchmarks/out/zoneStress/zone-stress.js", - "stress-inspect": "npm run build && npm run build-benchs && node --inspect benchmarks/out/zoneStress/zone-stress.js", "lint": "oxlint -c oxlintrc.json src", "lint-quiet": "oxlint --quiet -c oxlintrc.json src", "build": "npm run build-all", diff --git a/scripts/analyzeHeap.ts b/scripts/analyzeHeap.ts deleted file mode 100644 index effb3c10d8..0000000000 --- a/scripts/analyzeHeap.ts +++ /dev/null @@ -1,512 +0,0 @@ -#!/usr/bin/env npx tsx -// ====================================================================== -// -// GNU GENERAL PUBLIC LICENSE -// Version 3, 29 June 2007 -// copyright (C) 2020 - 2021 Quentin Gruber -// copyright (C) 2021 - 2026 H1emu community -// -// https://github.com/QuentinGruber/h1z1-server -// https://www.npmjs.com/package/h1z1-server -// -// Based on https://github.com/psemu/soe-network -// ====================================================================== -// -// Usage: -// npx tsx scripts/analyzeHeap.ts [heapprofile] [heaptimeline] -// -// Defaults to the paths passed on the command line or the constants below. -// ====================================================================== - -import * as fs from "fs"; -import * as path from "path"; -import { createReadStream } from "fs"; - -// ── Defaults ──────────────────────────────────────────────────────────────── -const DEFAULT_PROFILE = - "C:\\Users\\jason\\Downloads\\Heap-20260407T202017.heapprofile"; -const DEFAULT_TIMELINE = - "C:\\Users\\jason\\Downloads\\Heap-20260407T202309.heaptimeline"; - -const profilePath = process.argv[2] ?? DEFAULT_PROFILE; -const timelinePath = process.argv[3] ?? DEFAULT_TIMELINE; - -const TOP_N = 30; // how many entries to show in each section -const SRC_ROOT = path.join(__dirname, "..", "src"); -const PROJECT_URL_SUBSTR = "h1z1-server"; // used to identify project frames - -// ── Helpers ────────────────────────────────────────────────────────────────── -function fmt(bytes: number): string { - if (bytes >= 1_048_576) return `${(bytes / 1_048_576).toFixed(2)} MB`; - if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`; - return `${bytes} B`; -} - -function pct(part: number, total: number): string { - return total === 0 ? "0%" : `${((part / total) * 100).toFixed(1)}%`; -} - -function shortUrl(url: string): string { - const idx = url.indexOf(PROJECT_URL_SUBSTR); - if (idx !== -1) return url.slice(idx + PROJECT_URL_SUBSTR.length + 1); - // strip node internals prefix - return url.replace(/^node:/, "node:"); -} - -function header(title: string) { - const line = "═".repeat(70); - console.log(`\n${line}`); - console.log(` ${title}`); - console.log(line); -} - -function subheader(title: string) { - console.log(`\n── ${title} ${"─".repeat(Math.max(0, 66 - title.length))}`); -} - -// ── Part 1: Heap Sampling Profile (.heapprofile) ────────────────────────────── -interface CallFrame { - functionName: string; - url: string; - lineNumber: number; - columnNumber: number; - scriptId: string; -} -interface ProfileNode { - callFrame: CallFrame; - selfSize: number; - id: number; - children?: ProfileNode[]; -} -interface HeapProfile { - head: ProfileNode; - samples?: unknown[]; - startTime?: number; - endTime?: number; -} - -interface FlatAlloc { - key: string; - functionName: string; - url: string; - line: number; - selfSize: number; - isProject: boolean; -} - -function flattenProfileNode( - node: ProfileNode, - out: Map -): void { - if (node.selfSize > 0) { - const key = `${node.callFrame.url}:${node.callFrame.lineNumber}:${node.callFrame.functionName}`; - const existing = out.get(key); - if (existing) { - existing.selfSize += node.selfSize; - } else { - out.set(key, { - key, - functionName: - node.callFrame.functionName || "(anonymous)", - url: node.callFrame.url, - line: node.callFrame.lineNumber, - selfSize: node.selfSize, - isProject: node.callFrame.url.includes(PROJECT_URL_SUBSTR) - }); - } - } - for (const child of node.children ?? []) { - flattenProfileNode(child, out); - } -} - -function analyzeProfile(filePath: string) { - header("HEAP SAMPLING PROFILE (.heapprofile)"); - console.log(` File: ${filePath}`); - - const raw = fs.readFileSync(filePath, "utf8"); - const profile: HeapProfile = JSON.parse(raw); - - const flat = new Map(); - flattenProfileNode(profile.head, flat); - - const all = Array.from(flat.values()).sort( - (a, b) => b.selfSize - a.selfSize - ); - const totalSize = all.reduce((s, x) => s + x.selfSize, 0); - const projectFrames = all.filter((x) => x.isProject); - const projectTotal = projectFrames.reduce((s, x) => s + x.selfSize, 0); - - console.log(`\n Total sampled allocation: ${fmt(totalSize)}`); - console.log( - ` Project frames total: ${fmt(projectTotal)} (${pct(projectTotal, totalSize)} of all)` - ); - - subheader("Top allocation sites — ALL frames"); - console.log( - ` ${"Size".padStart(10)} ${"% total".padStart(7)} Location` - ); - for (const f of all.slice(0, TOP_N)) { - const loc = f.url - ? `${shortUrl(f.url)}:${f.line} ${f.functionName}` - : f.functionName; - console.log( - ` ${fmt(f.selfSize).padStart(10)} ${pct(f.selfSize, totalSize).padStart(7)} ${loc}` - ); - } - - subheader("Top allocation sites — PROJECT frames only"); - if (projectFrames.length === 0) { - console.log(" (none found — make sure the profile was taken from an out/ build)"); - } else { - console.log( - ` ${"Size".padStart(10)} ${"% proj".padStart(7)} Location` - ); - for (const f of projectFrames.slice(0, TOP_N)) { - const loc = `${shortUrl(f.url)}:${f.line} ${f.functionName}`; - console.log( - ` ${fmt(f.selfSize).padStart(10)} ${pct(f.selfSize, projectTotal).padStart(7)} ${loc}` - ); - } - } - - return { all, projectFrames, totalSize, projectTotal }; -} - -// ── Part 2: Heap Snapshot (.heaptimeline) ──────────────────────────────────── -// -// V8 heap snapshots use a compact flat encoding: -// nodes: flat array of (nodeFieldCount) integers per node -// edges: flat array of (edgeFieldCount) integers per edge -// strings: string table -// -// We do a streaming parse so we don't OOM on 337 MB JSON. - -interface SnapshotMeta { - node_fields: string[]; - node_types: (string | string[])[]; - edge_fields: string[]; - edge_types: (string | string[])[]; -} - -interface TypeStats { - count: number; - shallowSize: number; - retainedSize?: number; // too expensive to compute fully, skipped -} - -// Chunk-read and parse a huge JSON file using the `node_fields` layout. -// Rather than fully parsing the 337 MB file, we extract just what we need. -async function analyzeTimeline(filePath: string): Promise { - header("HEAP SNAPSHOT (.heaptimeline)"); - console.log(` File: ${filePath}`); - console.log(` Reading (this may take a moment for large snapshots)...`); - - const raw = await fs.promises.readFile(filePath, "utf8"); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const snap: any = JSON.parse(raw); - - const meta: SnapshotMeta = snap.snapshot.meta; - const nodeFields: string[] = meta.node_fields; // e.g. ["type","name","id","self_size","edge_count","trace_node_id","detachedness"] - const nodeTypes: string[] = meta.node_types[0] as string[]; // type name table - const strings: string[] = snap.strings; - const nodes: number[] = snap.nodes; - const nFields = nodeFields.length; - - const typeIdx = nodeFields.indexOf("type"); - const nameIdx = nodeFields.indexOf("name"); - const selfSizeIdx = nodeFields.indexOf("self_size"); - const edgeCountIdx = nodeFields.indexOf("edge_count"); - - const nodeCount = nodes.length / nFields; - console.log(` Node count: ${nodeCount.toLocaleString()}`); - - // ── Aggregate by type ──────────────────────────────────────────────────── - const byType = new Map(); - - let totalShallow = 0; - for (let i = 0; i < nodeCount; i++) { - const base = i * nFields; - const typeCode = nodes[base + typeIdx]; - const typeName = nodeTypes[typeCode] ?? `type_${typeCode}`; - const selfSize = nodes[base + selfSizeIdx]; - - const stat = byType.get(typeName); - if (stat) { - stat.count++; - stat.shallowSize += selfSize; - } else { - byType.set(typeName, { count: 1, shallowSize: selfSize }); - } - totalShallow += selfSize; - } - - subheader("Heap totals"); - console.log(` Total shallow size: ${fmt(totalShallow)}`); - console.log(` Node types found: ${byType.size}`); - - subheader(`Object types by shallow size (top ${TOP_N})`); - const typeRows = Array.from(byType.entries()).sort( - ([, a], [, b]) => b.shallowSize - a.shallowSize - ); - console.log( - ` ${"Type".padEnd(22)} ${"Count".padStart(10)} ${"Shallow".padStart(12)} ${"% total".padStart(7)}` - ); - for (const [name, stat] of typeRows.slice(0, TOP_N)) { - console.log( - ` ${name.padEnd(22)} ${stat.count.toLocaleString().padStart(10)} ${fmt(stat.shallowSize).padStart(12)} ${pct(stat.shallowSize, totalShallow).padStart(7)}` - ); - } - - subheader(`Object types by instance count (top ${TOP_N})`); - const countRows = Array.from(byType.entries()).sort( - ([, a], [, b]) => b.count - a.count - ); - console.log( - ` ${"Type".padEnd(22)} ${"Count".padStart(10)} ${"Shallow".padStart(12)} ${"Avg size".padStart(10)}` - ); - for (const [name, stat] of countRows.slice(0, TOP_N)) { - const avg = stat.count > 0 ? stat.shallowSize / stat.count : 0; - console.log( - ` ${name.padEnd(22)} ${stat.count.toLocaleString().padStart(10)} ${fmt(stat.shallowSize).padStart(12)} ${fmt(avg).padStart(10)}` - ); - } - - // ── String analysis ───────────────────────────────────────────────────── - subheader("String table analysis"); - const stringCount = strings.length; - const stringSizeBytes = strings.reduce((s, str) => s + str.length * 2, 0); // UTF-16 - console.log(` Unique strings in table: ${stringCount.toLocaleString()}`); - console.log(` Est. string table size: ${fmt(stringSizeBytes)} (UTF-16)`); - - // Find duplicate strings by value - const strFreq = new Map(); - for (const s of strings) { - strFreq.set(s, (strFreq.get(s) ?? 0) + 1); - } - // only deduplicated ones (same string appearing multiple times in table) - const duplicates = Array.from(strFreq.entries()) - .filter(([, count]) => count > 1) - .sort(([a, ca], [b, cb]) => { - // sort by wasted bytes (count-1) * len - const wasteA = (ca - 1) * a.length * 2; - const wasteB = (cb - 1) * b.length * 2; - return wasteB - wasteA; - }); - - if (duplicates.length > 0) { - console.log(`\n Top duplicated strings (wasted bytes):`); - console.log( - ` ${"Count".padStart(6)} ${"Wasted".padStart(10)} Value` - ); - for (const [str, count] of duplicates.slice(0, 20)) { - const wasted = (count - 1) * str.length * 2; - const preview = str.length > 60 ? str.slice(0, 57) + "..." : str; - console.log( - ` ${count.toString().padStart(6)} ${fmt(wasted).padStart(10)} ${JSON.stringify(preview)}` - ); - } - } - - // ── Closure / function analysis ────────────────────────────────────────── - subheader("Closure allocations by script URL"); - const edges: number[] = snap.edges; - const edgeFields: string[] = meta.edge_fields; - const edgeTypes: string[] = meta.edge_types[0] as string[]; - const eFields = edgeFields.length; - const eTypeIdx = edgeFields.indexOf("type"); - const eNameIdx = edgeFields.indexOf("name_or_index"); - const eToIdx = edgeFields.indexOf("to_node"); - - // Build a map from node index → script url for "code" / "closure" nodes - const closureTypeCode = nodeTypes.indexOf("closure"); - const codeTypeCode = nodeTypes.indexOf("code"); - - // For closures: find the "context" or "shared" edge → code → url isn't directly - // stored per node in snapshot format. Instead we look at "closure" nodes and - // find their name (which in V8 is the function name) and group them. - const closureByName = new Map(); - for (let i = 0; i < nodeCount; i++) { - const base = i * nFields; - const typeCode = nodes[base + typeIdx]; - if (typeCode !== closureTypeCode) continue; - const nameStringIdx = nodes[base + nameIdx]; - const name = strings[nameStringIdx] ?? "(anonymous)"; - const selfSize = nodes[base + selfSizeIdx]; - const stat = closureByName.get(name); - if (stat) { - stat.count++; - stat.size += selfSize; - } else { - closureByName.set(name, { count: 1, size: selfSize }); - } - } - - const closureRows = Array.from(closureByName.entries()).sort( - ([, a], [, b]) => b.size - a.size - ); - const totalClosureSize = closureRows.reduce((s, [, v]) => s + v.size, 0); - const totalClosureCount = closureRows.reduce((s, [, v]) => s + v.count, 0); - console.log( - ` Total closures: ${totalClosureCount.toLocaleString()} | Total size: ${fmt(totalClosureSize)}` - ); - console.log( - ` ${"Count".padStart(8)} ${"Size".padStart(12)} Name` - ); - for (const [name, stat] of closureRows.slice(0, TOP_N)) { - console.log( - ` ${stat.count.toString().padStart(8)} ${fmt(stat.size).padStart(12)} ${name}` - ); - } - - return; -} - -// ── Part 3: Recommendations ─────────────────────────────────────────────────── -function printRecommendations( - profileData: ReturnType | null -) { - header("OPTIMIZATION RECOMMENDATIONS"); - - const recs: Array<{ priority: "HIGH" | "MEDIUM" | "LOW"; title: string; detail: string }> = []; - - if (profileData) { - const { all, projectFrames, totalSize, projectTotal } = profileData; - - // Find the biggest single allocating project frame - const top = projectFrames[0]; - if (top && top.selfSize > totalSize * 0.05) { - recs.push({ - priority: "HIGH", - title: `Hot allocation: ${top.functionName} (${pct(top.selfSize, totalSize)} of all allocations)`, - detail: `${shortUrl(top.url)}:${top.line}\n This function is allocating a disproportionate amount of memory. Consider:\n - Pooling/reusing objects instead of allocating new ones each call\n - Moving large temporary buffers outside the hot path\n - Using typed arrays (Float32Array, Uint8Array) for numeric data` - }); - } - - // Look for Buffer / typed array sites - const bufferFrames = all.filter( - (f) => - f.functionName.toLowerCase().includes("buffer") || - f.url.toLowerCase().includes("buffer") - ); - if (bufferFrames.length > 0) { - const bufTotal = bufferFrames.reduce((s, f) => s + f.selfSize, 0); - if (bufTotal > totalSize * 0.1) { - recs.push({ - priority: "HIGH", - title: `Buffer allocations: ${fmt(bufTotal)} (${pct(bufTotal, totalSize)})`, - detail: `Buffer frames:\n${bufferFrames - .slice(0, 5) - .map((f) => ` - ${shortUrl(f.url)}:${f.line} ${f.functionName}`) - .join("\n")}\n Consider using a Buffer pool (e.g. buf.fill(0) + reuse) for packet encoding.` - }); - } - } - - // Look for zoneserver tick / client tick frames - const tickFrames = projectFrames.filter( - (f) => - f.functionName.toLowerCase().includes("tick") || - f.functionName.toLowerCase().includes("routine") || - f.functionName.toLowerCase().includes("spawn") - ); - if (tickFrames.length > 0) { - const tickTotal = tickFrames.reduce((s, f) => s + f.selfSize, 0); - recs.push({ - priority: "MEDIUM", - title: `Tick/routine allocations: ${fmt(tickTotal)} total`, - detail: `Hot tick paths:\n${tickFrames - .slice(0, 5) - .map( - (f) => - ` - ${shortUrl(f.url)}:${f.line} ${f.functionName} (${fmt(f.selfSize)})` - ) - .join("\n")}\n Allocations in tick functions run every frame. Pre-allocate and mutate.` - }); - } - } - - // General recommendations based on common patterns in this codebase - recs.push( - { - priority: "HIGH", - title: "Packet encoding: avoid per-packet Buffer.alloc()", - detail: `The packet encoding pipeline likely allocates a new Buffer for every outgoing packet.\n Use a single large pre-allocated Buffer per connection and write into a slice,\n or maintain a pool of fixed-size Buffers and recycle them after send().` - }, - { - priority: "HIGH", - title: "Entity position arrays: use Float32Array instead of number[]", - detail: `Every entity stores position/rotation as number[] (JS heap). Switching to Float32Array\n reduces memory by ~5× (4 bytes vs 8 bytes per float) and improves GC pressure.\n Affects all entities, vehicles, NPCs, construction, and projectiles.` - }, - { - priority: "HIGH", - title: "Map iteration in hot paths: cache .values() / .entries() arrays", - detail: `Calls like Object.values(_clients) or [...map.values()] inside tick loops create a\n new array every tick. Cache the live array when the collection rarely changes,\n or maintain a parallel array alongside the Map.` - }, - { - priority: "MEDIUM", - title: "String concatenation in loops: use template literals or join()", - detail: `If character IDs or packet headers are built via + concatenation inside loops,\n each intermediate string is heap-allocated. Use a pre-built string table or\n store IDs as numbers/typed arrays internally.` - }, - { - priority: "MEDIUM", - title: "Closure retention in event listeners: unbind unused listeners", - detail: `Long-lived event listeners (e.g., on('message')) that capture large scopes prevent\n GC. Store listener references and call removeListener() when the client disconnects.` - }, - { - priority: "MEDIUM", - title: "GridCell / spatial index: pre-size arrays to avoid re-allocation", - detail: `If gridcell.ts uses arrays that grow dynamically (push), they double in capacity on\n resize. Pre-size with new Array(expectedSize) or use typed arrays with a fixed\n max-per-cell limit.` - }, - { - priority: "LOW", - title: "JSON.stringify / JSON.parse in hot paths: use msgpack or binary protocol", - detail: `Any JSON serialization in the tick path (logging, world state snapshots, IPC)\n produces large transient strings. Consider msgpack-lite or a binary format.` - }, - { - priority: "LOW", - title: "Worker thread IPC: transfer ArrayBuffers instead of copying", - detail: `postMessage() with plain objects serializes via structured clone (copies memory).\n For large loot plan payloads, serialize into a SharedArrayBuffer or use\n transferable ArrayBuffers to avoid the copy overhead.` - } - ); - - const priorities: Array<"HIGH" | "MEDIUM" | "LOW"> = ["HIGH", "MEDIUM", "LOW"]; - for (const p of priorities) { - const group = recs.filter((r) => r.priority === p); - if (group.length === 0) continue; - subheader(`${p} priority`); - for (const rec of group) { - console.log(`\n [${rec.priority}] ${rec.title}`); - console.log(` ${rec.detail}`); - } - } -} - -// ── Main ────────────────────────────────────────────────────────────────────── -async function main() { - console.log("\nH1Z1-Server Heap Analysis Tool"); - console.log("================================\n"); - - let profileData: ReturnType | null = null; - - if (fs.existsSync(profilePath)) { - profileData = analyzeProfile(profilePath); - } else { - console.warn(`[WARN] heapprofile not found: ${profilePath}`); - } - - if (fs.existsSync(timelinePath)) { - await analyzeTimeline(timelinePath); - } else { - console.warn(`[WARN] heaptimeline not found: ${timelinePath}`); - } - - printRecommendations(profileData); - - console.log("\n"); -} - -main().catch((err) => { - console.error("Fatal:", err); - process.exit(1); -}); diff --git a/scripts/convertLootSpawns.ts b/scripts/convertLootSpawns.ts deleted file mode 100644 index 69ef20540c..0000000000 --- a/scripts/convertLootSpawns.ts +++ /dev/null @@ -1,95 +0,0 @@ -// ====================================================================== -// -// GNU GENERAL PUBLIC LICENSE -// Version 3, 29 June 2007 -// copyright (C) 2020 - 2021 Quentin Gruber -// copyright (C) 2021 - 2026 H1emu community -// -// https://github.com/QuentinGruber/h1z1-server -// https://www.npmjs.com/package/h1z1-server -// -// Based on https://github.com/psemu/soe-network -// ====================================================================== -// -// One-time migration script: converts lootspawns.ts → JSON loot table files. -// Run with: npx tsx scripts/convertLootSpawns.ts -// -// Output: -// data/2016/lootTables/ground/{actorDefinitionName}.json -// data/2016/lootTables/containers/{containerName}.json - -import fs from "fs"; -import path from "path"; -import { - lootTables, - containerLootSpawners -} from "../src/servers/ZoneServer2016/data/lootspawns"; -import type { - GroundLootTableJson, - ContainerLootTableJson, - LootPool, - LootTableEntry -} from "../src/types/zoneserver"; - -const ROOT = path.join(process.cwd(), "data", "2016", "lootTables"); -const GROUND_DIR = path.join(ROOT, "ground"); -const CONTAINER_DIR = path.join(ROOT, "containers"); - -fs.mkdirSync(GROUND_DIR, { recursive: true }); -fs.mkdirSync(CONTAINER_DIR, { recursive: true }); - -// ── Ground spawners ──────────────────────────────────────────────────────────── - -let groundCount = 0; -for (const [actorDefinition, spawner] of Object.entries(lootTables)) { - const pool: LootPool = { - conditions: [], - entries: spawner.items.map( - (def): LootTableEntry => ({ - item: def.item, - weight: def.weight, - count: { min: def.spawnCount.min, max: def.spawnCount.max } - }) - ) - }; - - const table: GroundLootTableJson = { - type: "ground", - spawnChance: spawner.spawnChance, - pools: [pool] - }; - - const fileName = path.join(GROUND_DIR, `${actorDefinition}.json`); - fs.writeFileSync(fileName, JSON.stringify(table, null, 2), "utf8"); - groundCount++; -} - -// ── Container spawners ───────────────────────────────────────────────────────── - -let containerCount = 0; -for (const [spawnerName, spawner] of Object.entries(containerLootSpawners)) { - const pool: LootPool = { - conditions: [], - entries: spawner.items.map( - (def): LootTableEntry => ({ - item: def.item, - weight: def.weight, - count: { min: def.spawnCount.min, max: def.spawnCount.max } - }) - ) - }; - - const table: ContainerLootTableJson = { - type: "container", - maxItems: spawner.maxItems, - pools: [pool] - }; - - const fileName = path.join(CONTAINER_DIR, `${spawnerName}.json`); - fs.writeFileSync(fileName, JSON.stringify(table, null, 2), "utf8"); - containerCount++; -} - -console.log( - `Migration complete: ${groundCount} ground tables, ${containerCount} container tables written to data/2016/lootTables/` -); diff --git a/src/packets/ClientProtocol/ClientProtocol_1080/base.ts b/src/packets/ClientProtocol/ClientProtocol_1080/base.ts index ff350a89d3..518fe5ccb2 100644 --- a/src/packets/ClientProtocol/ClientProtocol_1080/base.ts +++ b/src/packets/ClientProtocol/ClientProtocol_1080/base.ts @@ -2948,30 +2948,8 @@ export const basePackets: PacketStructures = [ ] } ], - [ - "PlayerUpdate.AttachObject", - 0xae, - { - fields: [ - { name: "gameTick", type: "uint32", defaultValue: 0 }, - { name: "sourceCharacter", type: "uint64string", defaultValue: "0" }, - { name: "targetCharacter", type: "uint64string", defaultValue: "0" }, - { - name: "unknownFloatVector1", - type: "floatvector4", - defaultValue: [0, 0, 0, 0] - }, - { - name: "unknownFloatVector2", - type: "floatvector4", - defaultValue: [0, 0, 0, 0] - }, - { name: "unknownDword2", type: "uint32", defaultValue: 0 }, - { name: "unknownShort1", type: "uint16", defaultValue: 0 }, - { name: "unknownDword3", type: "uint32", defaultValue: 0 } // dependant on unknownShort1 - ] - } - ], + + ["PlayerUpdate.AttachObject", 0xae, {}], ["PlayerUpdate.DetachObject", 0xaf, {}], [ "ClientSettings", diff --git a/src/packets/ClientProtocol/ClientProtocol_1080/replication.ts b/src/packets/ClientProtocol/ClientProtocol_1080/replication.ts index 0c8c527441..4db6d13cec 100644 --- a/src/packets/ClientProtocol/ClientProtocol_1080/replication.ts +++ b/src/packets/ClientProtocol/ClientProtocol_1080/replication.ts @@ -11,9 +11,8 @@ // Based on https://github.com/psemu/soe-network // ====================================================================== -import { PacketFields, PacketStructures } from "types/packetStructure"; +import { PacketStructures } from "types/packetStructure"; import { packUnsignedIntWith2bitLengthValue } from "./shared"; -import DataSchema from "h1z1-dataschema"; export function packComponentNameString(name: string) { const stringBuffer = Buffer.from(name.trim(), "ascii"), @@ -23,24 +22,18 @@ export function packComponentNameString(name: string) { return Buffer.concat([lengthBuffer, stringBuffer, nullBuffer]); } -export const npcComponent: PacketFields = [ - { name: "unknownDword1", type: "uint32", defaultValue: 634 }, - { name: "unknownDword2", type: "uint32", defaultValue: 0 }, - { name: "unknownDword3", type: "uint32", defaultValue: 0 }, - { name: "nameId", type: "uint32", defaultValue: 0 }, - { name: "unknownDword5", type: "uint32", defaultValue: 0 }, - { name: "unknownDword6", type: "uint32", defaultValue: 0 }, - { name: "unknownDword7", type: "uint32", defaultValue: 0 }, - { name: "unknownFloatVector1", type: "floatvector3", defaultValue: 0 }, - { name: "unknownFloatVector2", type: "floatvector3", defaultValue: 0 }, - { name: "unknownDword8", type: "uint32", defaultValue: 0 }, - { name: "unknownDword9", type: "uint32", defaultValue: 0 }, - { name: "unknownQword1", type: "uint64string", defaultValue: "0" }, - { name: "unknownDword10", type: "uint32", defaultValue: 0 }, - { name: "unknownDword11", type: "uint32", defaultValue: 0 }, - { name: "unknownByte1", type: "uint8", defaultValue: 0 }, - { name: "unknownBoolean1", type: "boolean", defaultValue: true } -]; +export function packClientNpcComponent(obj: any) { + const raw: Buffer = Buffer.alloc(78); + if (obj["nameId"]) { + raw.writeUInt32LE(obj["nameId"], 12); + } + + raw.writeUint8(122, 0); + raw.writeUint8(2, 1); + + raw.writeUint8(1, raw.length - 1); + return raw; +} export function packClientInteractComponent(obj: any) { const raw: Buffer = Buffer.alloc(11); @@ -59,7 +52,7 @@ export function packClientInteractComponent(obj: any) { export function packNpcComponent(obj: any) { switch (obj.componentName) { case "ClientNpcComponent": - return DataSchema.pack(npcComponent, obj).data; + return packClientNpcComponent(obj); case "ClientInteractComponent": return packClientInteractComponent(obj); default: diff --git a/src/packets/ClientProtocol/ClientProtocol_1080/vehicle.ts b/src/packets/ClientProtocol/ClientProtocol_1080/vehicle.ts index 5ea35c94f2..0b8c0c7dcd 100644 --- a/src/packets/ClientProtocol/ClientProtocol_1080/vehicle.ts +++ b/src/packets/ClientProtocol/ClientProtocol_1080/vehicle.ts @@ -93,7 +93,7 @@ export const vehiclePackets: PacketStructures = [ { fields: [ { name: "guid", type: "uint64string", defaultValue: "0" }, - { name: "gameTick", type: "uint32", defaultValue: 0 }, + { name: "unknownDword1", type: "uint32", defaultValue: 0 }, { name: "unknownArray1", type: "array", diff --git a/src/packets/LoginUdp/LoginUdp_11/loginpackets.ts b/src/packets/LoginUdp/LoginUdp_11/loginpackets.ts index 9717c4fb04..06a3aa4913 100644 --- a/src/packets/LoginUdp/LoginUdp_11/loginpackets.ts +++ b/src/packets/LoginUdp/LoginUdp_11/loginpackets.ts @@ -176,7 +176,7 @@ const packets: PacketStructures = [ ] }, { - name: "errorDetails", + name: "unknownArray2", type: "array", fields: [ { name: "unknownString1", type: "string", defaultValue: "" }, diff --git a/src/protocols/lzconnectionprotocol.ts b/src/protocols/lzconnectionprotocol.ts index 44f66729e1..933b675be1 100644 --- a/src/protocols/lzconnectionprotocol.ts +++ b/src/protocols/lzconnectionprotocol.ts @@ -217,41 +217,6 @@ const packets: PacketStructures = [ } ] } - ], - [ - "GlobalBroadcastRequest", - 0x1a, - { - fields: [ - { name: "broadcastType", type: "uint8", defaultValue: 0 }, - { name: "initiatorName", type: "string", defaultValue: "" }, - { name: "message", type: "string", defaultValue: "" }, - { - name: "rewardIds", - type: "array", - defaultValue: [], - fields: [{ name: "rewardId", type: "uint32", defaultValue: 0 }] - } - ] - } - ], - [ - "GlobalBroadcastForward", - 0x1b, - { - fields: [ - { name: "broadcastType", type: "uint8", defaultValue: 0 }, - { name: "initiatorName", type: "string", defaultValue: "" }, - { name: "message", type: "string", defaultValue: "" }, - { name: "originServerId", type: "uint32", defaultValue: 0 }, - { - name: "rewardIds", - type: "array", - defaultValue: [], - fields: [{ name: "rewardId", type: "uint32", defaultValue: 0 }] - } - ] - } ] ]; diff --git a/src/servers/GatewayServer/gatewayserver.threaded.ts b/src/servers/GatewayServer/gatewayserver.threaded.ts index b26f9683ea..0081c2ef47 100644 --- a/src/servers/GatewayServer/gatewayserver.threaded.ts +++ b/src/servers/GatewayServer/gatewayserver.threaded.ts @@ -123,11 +123,6 @@ export class GatewayServerThreaded extends EventEmitter { return this.askGatewayThread(fnName, soeClientId); } - async getServerNetworkStats(): Promise { - const fnName = this.getServerNetworkStats.name; - return this.askGatewayThread(fnName, ""); - } - private askGatewayThread(fnName: string, soeClientId: string): Promise { const reqId = this.reqCount++; this.clientInfoChannel.port2.postMessage({ diff --git a/src/servers/LoginServer/loginserver.ts b/src/servers/LoginServer/loginserver.ts index 584dbb6a73..c075c06a14 100644 --- a/src/servers/LoginServer/loginserver.ts +++ b/src/servers/LoginServer/loginserver.ts @@ -65,19 +65,12 @@ import { import DataSchema from "h1z1-dataschema"; import { applicationDataKOTK } from "../../packets/LoginUdp/LoginUdp_11/loginpackets"; import { Resolver } from "node:dns"; -import { PluginManager } from "../../servers/ZoneServer2016/managers/pluginmanager"; const debugName = "LoginServer"; const debug = require("debug")(debugName); -const characterItemDefinitionsDummy = PluginManager.loadServerData( - "2015/sampleData/characterItemDefinitionsDummy.json" -); -const defaultHashes: Array = PluginManager.loadServerData( - "2016/dataSources/AllowedFileHashes.json" -); -const loginReply2016 = PluginManager.loadServerData( - "2016/dataSources/LoginData.json" -); +const characterItemDefinitionsDummy = require("../../../data/2015/sampleData/characterItemDefinitionsDummy.json"); +const defaultHashes: Array = require("../../../data/2016/dataSources/AllowedFileHashes.json"); +const loginReply2016 = require("../../../data/2016/dataSources/LoginData.json"); export enum LoginStatus { REJECTED = 0, @@ -110,7 +103,6 @@ export class LoginServer extends EventEmitter { _db!: Db; _crcLength: crc_length_options; _udpLength: number; - pluginManager: PluginManager; private readonly _cryptoKey: Uint8Array; private readonly _mongoAddress: string; private readonly _soloMode: boolean; @@ -120,12 +112,6 @@ export class LoginServer extends EventEmitter { _httpServerPort: number = Number(process.env.HTTP_PORT ?? 80); private _zoneConnectionManager!: ZoneConnectionManager; private _zoneConnections: { [LZConnectionClientId: string]: number } = {}; - private _globalBroadcastAllowedZones: Set = new Set( - (process.env.GLOBAL_BROADCAST_SERVER_IDS || "") - .split(",") - .map(Number) - .filter((id) => id > 0) - ); private _internalReqCount: number = 0; private _pendingInternalReq: { [requestId: number]: any } = {}; private _pendingInternalReqTimeouts: { [requestId: number]: NodeJS.Timeout } = @@ -151,7 +137,7 @@ export class LoginServer extends EventEmitter { this._loginQueuesTimer = setInterval(() => { this.updateLoginQueues(); }, this._loginRate / 2); - this.pluginManager = new PluginManager(); + // reminders if (!this._mongoAddress) { this._soloMode = true; @@ -318,37 +304,6 @@ export class LoginServer extends EventEmitter { }); break; } - case "GlobalBroadcastRequest": { - const originServerId = this._zoneConnections[client.clientId]; - if ( - !originServerId || - !this._globalBroadcastAllowedZones.has(originServerId) - ) - return; - const { broadcastType, initiatorName, message, rewardIds } = - packet.data; - for (const zoneClientId in this._zoneConnections) { - const zoneServerId = this._zoneConnections[zoneClientId]; - if (!this._globalBroadcastAllowedZones.has(zoneServerId)) - continue; - const zoneClient = - this._zoneConnectionManager._clients[zoneClientId]; - if (zoneClient) { - this._zoneConnectionManager.sendData( - zoneClient, - "GlobalBroadcastForward", - { - broadcastType, - initiatorName, - message, - originServerId, - rewardIds - } - ); - } - } - break; - } default: console.log( `Unhandled ZoneConnection packet: ${packet.name}` @@ -406,7 +361,6 @@ export class LoginServer extends EventEmitter { this._zoneConnectionManager.start(); } - this.pluginManager.initializePlugins(this); } async getGuidByAuthkey(authKey: string): Promise { @@ -588,7 +542,7 @@ export class LoginServer extends EventEmitter { } } ], - errorDetails: [], + unknownArray2: [], ipCountryCode: "US", applicationPayload: { unknownArray1: [], @@ -879,16 +833,9 @@ export class LoginServer extends EventEmitter { gameVersion: client.gameVersion }) .toArray(); - const loginSessionId = await this.getGuidByAuthkey(client.authKey); - const isAdmin = Boolean( - loginSessionId && - (await this._db - .collection(DB_COLLECTIONS.ADMINS) - .findOne({ sessionId: loginSessionId, permissionLevel: { $ne: 0 } })) - ); servers = servers .map((server: GameServer) => { - if (server.locked && !isAdmin) { + if (server.locked) { server.allowedAccess = false; } return server; @@ -900,23 +847,17 @@ export class LoginServer extends EventEmitter { switch (client.gameVersion) { default: case GAME_VERSIONS.H1Z1_15janv_2015: { - const SoloServer = PluginManager.loadServerData( - "2015/sampleData/single_player_server.json" - ); + const SoloServer = require("../../../data/2015/sampleData/single_player_server.json"); servers = [SoloServer]; break; } case GAME_VERSIONS.H1Z1_6dec_2016: { - const SoloServer = PluginManager.loadServerData( - "2016/sampleData/single_player_server.json" - ); + const SoloServer = require("../../../data/2016/sampleData/single_player_server.json"); servers = [SoloServer]; break; } case GAME_VERSIONS.H1Z1_KOTK_PS3: { - const SoloServer = PluginManager.loadServerData( - "kotk/sampleData/single_player_server.json" - ); + const SoloServer = require("../../../data/kotk/sampleData/single_player_server.json"); servers = [SoloServer]; break; } @@ -1437,9 +1378,7 @@ export class LoginServer extends EventEmitter { let sampleCharacter, newCharacter; switch (client.gameVersion) { case GAME_VERSIONS.H1Z1_15janv_2015: { - sampleCharacter = PluginManager.loadServerData( - "2015/sampleData/single_player_character.json" - ); + sampleCharacter = require("../../../data/2015/sampleData/single_player_character.json"); newCharacter = _.cloneDeep(sampleCharacter) as any; newCharacter.payload.name = characterName; break; @@ -1447,9 +1386,7 @@ export class LoginServer extends EventEmitter { default: case GAME_VERSIONS.H1Z1_KOTK_PS3: case GAME_VERSIONS.H1Z1_6dec_2016: { - sampleCharacter = PluginManager.loadServerData( - "2016/sampleData/character.json" - ); + sampleCharacter = require("../../../data/2016/sampleData/character.json"); newCharacter = _.cloneDeep(sampleCharacter) as any; newCharacter.characterName = characterName; break; @@ -1655,8 +1592,5 @@ export class LoginServer extends EventEmitter { if (process.env.VSCODE_DEBUG === "true") { const PackageSetting = require("../../../package.json"); process.env.H1Z1_SERVER_VERSION = PackageSetting.version; - new LoginServer( - Number(process.env.SERVER_BIND_PORT) || 1115, - process.env.MONGO_URL - ).start(); + new LoginServer(1115, process.env.MONGO_URL).start(); } diff --git a/src/servers/SoeServer/soeclient.ts b/src/servers/SoeServer/soeclient.ts index 574a80f26d..209fcbf319 100644 --- a/src/servers/SoeServer/soeclient.ts +++ b/src/servers/SoeServer/soeclient.ts @@ -90,13 +90,6 @@ export default class SOEClient { packetResend, packetsOutOfOrder } = this.stats; - if (totalPacketSent === 0) { - return [ - `Packet loss rate 0%`, - `Packet outOfOrder rate 0%`, - `Avg ping ${this.avgPing}ms` - ]; - } const packetLossRate = Number((packetResend / totalPacketSent).toFixed(3)) * 100; const packetOutOfOrderRate = diff --git a/src/servers/SoeServer/soeserver.ts b/src/servers/SoeServer/soeserver.ts index e784581181..83b74e2528 100644 --- a/src/servers/SoeServer/soeserver.ts +++ b/src/servers/SoeServer/soeserver.ts @@ -24,15 +24,6 @@ import dgram from "node:dgram"; import { PacketsQueue } from "./PacketsQueue"; const debug = require("debug")("SOEServer"); -const MAX_PACKETS_PER_IP_PER_SEC = 500; -const MAX_SESSIONS_PER_IP_PER_MIN = 5; - -interface RateWindow { - count: number; - windowStart: number; - logged: boolean; -} - export class SOEServer extends EventEmitter { _serverAddress: string; _serverAddressV6: string; @@ -56,8 +47,6 @@ export class SOEServer extends EventEmitter { avgEventLoopLag: number = 0; eventLoopLagValues: number[] = []; currentEventLoopLag: number = 0; - private _ipRateMap: Map = new Map(); - private _sessionRequestMap: Map = new Map(); constructor(serverPort: number, cryptoKey: Uint8Array) { super(); const oneMb = 1024 * 1024; @@ -418,50 +407,8 @@ export class SOEServer extends EventEmitter { } } - private _checkIpRateLimit(ip: string): boolean { - const now = Date.now(); - let entry = this._ipRateMap.get(ip); - if (!entry || now - entry.windowStart > 1000) { - entry = { count: 0, windowStart: now, logged: false }; - this._ipRateMap.set(ip, entry); - } - entry.count++; - if (entry.count > MAX_PACKETS_PER_IP_PER_SEC) { - if (!entry.logged) { - console.log( - `[DDoS] IP ${ip} rate-limited: ${entry.count} packets in 1s window (dropped)` - ); - entry.logged = true; - } - return false; - } - return true; - } - - private _checkSessionRateLimit(ip: string): boolean { - const now = Date.now(); - let entry = this._sessionRequestMap.get(ip); - if (!entry || now - entry.windowStart > 60000) { - entry = { count: 0, windowStart: now, logged: false }; - this._sessionRequestMap.set(ip, entry); - } - entry.count++; - if (entry.count > MAX_SESSIONS_PER_IP_PER_MIN) { - if (!entry.logged) { - console.log( - `[DDoS] IP ${ip} session-spam: ${entry.count} session requests in 60s (dropped)` - ); - entry.logged = true; - } - return false; - } - return true; - } - onMessage(data: Buffer, remote: RemoteInfo) { try { - if (!this._checkIpRateLimit(remote.address)) return; - let client: SOEClient; const clientId = SOEClient.getClientId(remote); debug(data.length + " bytes from ", clientId); @@ -471,7 +418,6 @@ export class SOEServer extends EventEmitter { if (data[1] !== 1) { return; } - if (!this._checkSessionRateLimit(remote.address)) return; client = this._createClient(remote); } else { client = this._clients.get(clientId) as SOEClient; @@ -518,16 +464,6 @@ export class SOEServer extends EventEmitter { if (udpLength !== undefined) { this._udpLength = udpLength; } - setInterval(() => { - const now = Date.now(); - for (const [ip, entry] of this._ipRateMap) { - if (now - entry.windowStart > 2000) this._ipRateMap.delete(ip); - } - for (const [ip, entry] of this._sessionRequestMap) { - if (now - entry.windowStart > 62000) this._sessionRequestMap.delete(ip); - } - }, 60000); - this._connection.on("message", (data, remote) => { this.onMessage(data, remote); }); @@ -652,7 +588,7 @@ export class SOEServer extends EventEmitter { } } client.unAckData.delete(resend.sequence as number); - if (client.stats.packetResend > 0) client.stats.packetResend--; + client.stats.packetResend--; } if (client.outputStream.isReliableAvailable()) { const appPackets = this.getAvailableAppPackets(client); diff --git a/src/servers/ZoneServer2016/classes/gridcell.ts b/src/servers/ZoneServer2016/classes/gridcell.ts index c27577313e..b8a1844133 100644 --- a/src/servers/ZoneServer2016/classes/gridcell.ts +++ b/src/servers/ZoneServer2016/classes/gridcell.ts @@ -1,23 +1,13 @@ import { BaseEntity } from "../entities/baseentity"; -import { ZoneServer2016 } from "../zoneserver"; /** Used in splitting the map into the appropriate grids based upon the image */ export class GridCell { position: Float32Array; objects: Array = []; - /** Clients currently subscribed to this cell for event-driven spawn/despawn */ - subscribers: Set = new Set(); width: number; height: number; - availableScrap: number; - constructor( - server: ZoneServer2016, - x: number, - y: number, - width: number, - height: number - ) { - this.availableScrap = server.worldObjectManager.gridScrapLimit; + availableScrap: number = 50; + constructor(x: number, y: number, width: number, height: number) { this.position = new Float32Array([x, 0, y, 1]); this.width = width; this.height = height; diff --git a/src/servers/ZoneServer2016/classes/trackedentityset.ts b/src/servers/ZoneServer2016/classes/trackedentityset.ts deleted file mode 100644 index 6437f9d639..0000000000 --- a/src/servers/ZoneServer2016/classes/trackedentityset.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { BaseEntity } from "../entities/baseentity"; - -/** - * A Set that keeps an external inverse-observer registry in sync. - * The registry maps entity.characterId → Set so callers can look up - * which owners have a given entity without iterating the full owner list. - */ -export class TrackedEntitySet extends Set { - constructor( - private readonly _registry: Map>, - private readonly _owner: TOwner - ) { - super(); - } - - add(entity: BaseEntity): this { - if (!super.has(entity)) { - super.add(entity); - let obs = this._registry.get(entity.characterId); - if (!obs) { - obs = new Set(); - this._registry.set(entity.characterId, obs); - } - obs.add(this._owner); - } - return this; - } - - delete(entity: BaseEntity): boolean { - if (super.delete(entity)) { - this._registry.get(entity.characterId)?.delete(this._owner); - return true; - } - return false; - } - - clear(): void { - for (const entity of this) { - this._registry.get(entity.characterId)?.delete(this._owner); - } - super.clear(); - } -} diff --git a/src/servers/ZoneServer2016/classes/zoneclient.ts b/src/servers/ZoneServer2016/classes/zoneclient.ts index c3d0b67ccc..fdae3e4ec8 100644 --- a/src/servers/ZoneServer2016/classes/zoneclient.ts +++ b/src/servers/ZoneServer2016/classes/zoneclient.ts @@ -19,7 +19,6 @@ import { ZoneServer2016 } from "../zoneserver"; import { BaseEntity } from "../entities/baseentity"; import { FireHint } from "../../../types/zoneserver"; import { Lootbag } from "../entities/lootbag"; -import { TrackedEntitySet } from "./trackedentityset"; //import { h1z1PacketsType2016 } from "../../../types/packets"; //import { zone2016packets } from "../../../types/zone2016packets"; @@ -39,8 +38,6 @@ export class ZoneClient2016 { banType: string = ""; HWID: string = ""; posAtLastRoutine: Float32Array = new Float32Array(); - posAtLastPermissionCheck: Float32Array = new Float32Array(); - lastRoutineTime: number = 0; posAtTimerStart: Float32Array = new Float32Array(); oldPos: { position: Float32Array; time: number } = { position: new Float32Array(), @@ -89,17 +86,13 @@ export class ZoneClient2016 { lastMovementImpared: number = 0; avgPingReady: boolean = false; chunkRenderDistance: number = 400; + routineCounter: number = 0; zonePings: number[] = []; properlyLogout: boolean = false; permissionLevel: number = 0; fireHints: { [id: number]: FireHint } = {}; isInAir: boolean = false; startLoc: number = 0; - fairPlayFoundationCheckResult: boolean = false; - fairPlayFoundationCheckTime: number = 0; - subscribedCells: Set = new Set(); - posAtLastCellUpdate: Float32Array = new Float32Array([0, 0, 0, 1]); - lastKnownChunkRenderDistance: number = 400; startingPos?: Float32Array; firstReleased: boolean = true; /*(lightWeightNpcQueue: { @@ -114,15 +107,11 @@ export class ZoneClient2016 { isInVoiceChat: boolean = false; voiceChatTimer?: NodeJS.Timeout; heartBeatTimer?: NodeJS.Timeout; - pingTimer?: NodeJS.Timeout; - fairPlayTimer?: NodeJS.Timeout; afkTimer?: NodeJS.Timeout; movementSet: Set = new Set(); static minMovementForAfk: number = 20; static afkTime: number = 10 * 60_000; gotAfkWarning: boolean = false; - heavyPacketCount: number = 0; - heavyPacketWindowStart: number = 0; constructor( sessionId: number, soeClientId: string, @@ -135,10 +124,7 @@ export class ZoneClient2016 { this.soeClientId = soeClientId; this.loginSessionId = loginSessionId; - this.spawnedEntities = new TrackedEntitySet( - server._entityObservers, - this - ); + this.spawnedEntities = new Set(); this.managedObjects = []; this.clearTimers = () => { clearTimeout(this.npcsToSpawnTimer); diff --git a/src/servers/ZoneServer2016/data/loadouts.ts b/src/servers/ZoneServer2016/data/loadouts.ts index 537697ce37..f5bfe0f9e1 100644 --- a/src/servers/ZoneServer2016/data/loadouts.ts +++ b/src/servers/ZoneServer2016/data/loadouts.ts @@ -153,13 +153,6 @@ export const characterBuildKitLoadout = [ { item: Items.GROUND_TAMPER, count: 10 }, { item: Items.WEAPON_HAMMER_DEMOLITION } ]; -export const characterFarmKitLoadout = [ - { item: Items.GROUND_TAMPER, count: 10 }, - { item: Items.GROUND_TILLER, count: 400 }, - { item: Items.SEED_CORN, count: 400 }, - { item: Items.SEED_WHEAT, count: 400 }, - { item: Items.FERTILIZER, count: 400 } -]; export const characterTestKitLoadout = [ { item: Items.FOUNDATION, count: 10 }, diff --git a/src/servers/ZoneServer2016/data/lootspawns.ts b/src/servers/ZoneServer2016/data/lootspawns.ts new file mode 100644 index 0000000000..64943fb7e2 --- /dev/null +++ b/src/servers/ZoneServer2016/data/lootspawns.ts @@ -0,0 +1,4269 @@ +// ====================================================================== +// +// GNU GENERAL PUBLIC LICENSE +// Version 3, 29 June 2007 +// copyright (C) 2020 - 2021 Quentin Gruber +// copyright (C) 2021 - 2026 H1emu community +// +// https://github.com/QuentinGruber/h1z1-server +// https://www.npmjs.com/package/h1z1-server +// +// Based on https://github.com/psemu/soe-network +// ====================================================================== + +import { ContainerLootSpawner, LootSpawner } from "types/zoneserver"; +import { Items } from "../models/enums"; + +const carparts = [ + // NEED TO ADJUST THESE WEIGHTS + { + item: Items.BATTERY, + weight: 5, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.SPARKPLUGS, + weight: 5, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.HEADLIGHTS_OFFROADER, + weight: 5, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.HEADLIGHTS_POLICE, + weight: 5, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.HEADLIGHTS_ATV, + weight: 5, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.HEADLIGHTS_PICKUP, + weight: 5, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.TURBO_OFFROADER, + weight: 5, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.TURBO_POLICE, + weight: 5, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.TURBO_ATV, + weight: 5, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.TURBO_PICKUP, + weight: 5, + spawnCount: { + min: 1, + max: 1 + } + } +]; + +export const lootTables: { [lootSpawner: string]: LootSpawner } = { + // #region AR15 + "ItemSpawner_Weapon_M16A4.adr": { + spawnChance: 40, + items: [ + { + item: Items.WEAPON_AR15, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_AmmoBox02_M16A4.adr": { + spawnChance: 40, + items: [ + { + item: Items.AMMO_223, + weight: 100, + spawnCount: { + min: 1, + max: 4 + } + } + ] + }, + "ItemSpawner_AmmoBox02.adr": { + spawnChance: 20, + items: [ + { + item: Items.AMMO_45, + weight: 100, + spawnCount: { + min: 1, + max: 4 + } + } + ] + }, + // #endregion + + // #region SHOTGUN + "ItemSpawner_Weapon_PumpShotgun01.adr": { + spawnChance: 40, + items: [ + { + item: Items.WEAPON_SHOTGUN, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_AmmoBox02_12GaShotgun.adr": { + spawnChance: 40, + items: [ + { + item: Items.AMMO_12GA, + weight: 100, + spawnCount: { + min: 1, + max: 4 + } + } + ] + }, + // #endregion + + // #region TOOLS + "ItemSpawner_Weapon_Crowbar01.adr": { + spawnChance: 35, + items: [ + { + item: Items.WEAPON_CROWBAR, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_Weapon_CombatKnife01.adr": { + spawnChance: 20, + items: [ + { + item: Items.WEAPON_COMBATKNIFE, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_Weapon_Machete01.adr": { + spawnChance: 20, + items: [ + { + item: Items.WEAPON_MACHETE01, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + // did not spawn in this game version but why not + item: Items.WEAPON_KATANA, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_Weapon_Bat01.adr": { + spawnChance: 10, + items: [ + { + item: Items.WEAPON_BAT_WOOD, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_Weapon_Guitar01.adr": { + spawnChance: 20, + items: [ + { + item: Items.WEAPON_GUITAR, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_Weapon_WoodAxe01.adr": { + spawnChance: 20, + items: [ + { + item: Items.WEAPON_AXE_WOOD, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_Weapon_FireAxe01.adr": { + spawnChance: 20, + items: [ + { + item: Items.WEAPON_AXE_FIRE, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_Weapon_ClawHammer01.adr": { + spawnChance: 20, + items: [ + { + item: Items.WEAPON_HAMMER, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_Weapon_Hatchet01.adr": { + spawnChance: 20, + items: [ + { + item: Items.WEAPON_HATCHET, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_Weapon_Pipe01.adr": { + spawnChance: 20, + items: [ + { + item: Items.WEAPON_PIPE, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_Weapon_Bat02.adr": { + spawnChance: 10, + items: [ + { + item: Items.WEAPON_BAT_ALUM, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_Weapon_Bow.adr": { + spawnChance: 20, + items: [ + { + item: Items.WEAPON_BOW_MAKESHIFT, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_BOW_RECURVE, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + // #endregion + + // #region PISTOLS + "ItemSpawner_Weapon_45Auto.adr": { + spawnChance: 20, + items: [ + { + item: Items.WEAPON_1911, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_Weapon_M9Auto.adr": { + // need to find 9MM ammo spawner + spawnChance: 20, + items: [ + { + item: Items.WEAPON_M9, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_AmmoBox02_1911.adr": { + spawnChance: 20, + items: [ + { + item: Items.AMMO_45, + weight: 100, + spawnCount: { + min: 1, + max: 6 + } + } + ] + }, + // #endregion + + // #region 308 RIFLE + "ItemSpawner_Weapon_M24.adr": { + spawnChance: 50, + items: [ + { + item: Items.WEAPON_308, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_AmmoBox02_308Rifle.adr": { + spawnChance: 40, + items: [ + { + item: Items.AMMO_308, + weight: 100, + spawnCount: { + min: 1, + max: 4 + } + } + ] + }, + // #endregion + + // #region CONSUMABLES + "ItemSpawner_FirstAidKit.adr": { + spawnChance: 5, + items: [ + { + item: Items.FIRST_AID, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_CannedFood.adr": { + spawnChance: 20, + items: [ + { + item: Items.GROUND_COFFEE, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD01, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_WaterContainer_Small_Purified.adr": { + spawnChance: 10, + items: [ + { + item: Items.WATER_PURE, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + // #endregion + + // #region CLOTHING + "ItemSpawner_Clothes_MotorcycleHelmet.adr": { + spawnChance: 10, + items: [ + { + item: Items.HELMET_MOTORCYCLE, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_Clothes_Helmet_Tactical_ChipsScratches.adr": { + spawnChance: 10, + items: [ + { + item: Items.DEFAULT_TACTICAL_HELMET, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_Clothes_BaseballCap.adr": { + spawnChance: 20, + items: [ + { + item: Items.HAT_CAP, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_Clothes_FoldedShirt.adr": { + spawnChance: 20, + items: [ + { + item: Items.SHIRT_DEFAULT, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.PANTS_DEFAULT, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_Clothes_Beanie.adr": { + spawnChance: 20, + items: [ + { + item: Items.HAT_BEANIE, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + // #endregion + + // #region RESIDENTIAL + "ItemSpawnerResidential_Tier00.adr": { + spawnChance: 20, + items: [ + { + item: Items.WEAPON_308, + weight: 6, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_SHOTGUN, + weight: 6, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_1911, + weight: 7, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_M9, + weight: 7, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.DUCT_TAPE, + weight: 20, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.TWINE, + weight: 20, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.SHIRT_DEFAULT, + weight: 40, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.PANTS_DEFAULT, + weight: 40, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CONVEYS_BLUE, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.BATTERY, + weight: 10, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_COMBATKNIFE, + weight: 15, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.HAT_CAP, + weight: 15, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.HAT_BEANIE, + weight: 15, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.HELMET_MOTORCYCLE, + weight: 15, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD01, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_REPAIR_KIT, + weight: 5, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WATER_EMPTY, + weight: 25, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WATER_DIRTY, + weight: 15, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.AMMO_45, + weight: 15, + spawnCount: { + min: 1, + max: 4 + } + }, + { + item: Items.AMMO_9MM, + weight: 15, + spawnCount: { + min: 1, + max: 4 + } + }, + { + item: Items.AMMO_380, + weight: 15, + spawnCount: { + min: 1, + max: 4 + } + }, + { + item: Items.AMMO_44, + weight: 11, + spawnCount: { + min: 1, + max: 4 + } + }, + { + item: Items.AMMO_223, + weight: 3, + spawnCount: { + min: 1, + max: 5 + } + }, + { + item: Items.AMMO_762, + weight: 3, + spawnCount: { + min: 1, + max: 5 + } + }, + { + item: Items.AMMO_308, + weight: 3, + spawnCount: { + min: 1, + max: 4 + } + }, + { + item: Items.AMMO_12GA, + weight: 3, + spawnCount: { + min: 1, + max: 4 + } + }, + { + item: Items.SPARKPLUGS, + weight: 10, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.FIRST_AID, + weight: 15, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_BINOCULARS, + weight: 20, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_BAT_WOOD, + weight: 10, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_BAT_ALUM, + weight: 10, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.BACKPACK_BLUE_ORANGE, + weight: 8, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.EMERGENCY_RADIO, + weight: 7, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + // #endregion + + // #region RARE + "ItemSpawnerRare_Tier00.adr": { + spawnChance: 15, + items: [ + { + item: Items.AMMO_45, + weight: 120, + spawnCount: { + min: 1, + max: 4 + } + }, + { + item: Items.AMMO_9MM, + weight: 120, + spawnCount: { + min: 1, + max: 4 + } + }, + { + item: Items.AMMO_380, + weight: 120, + spawnCount: { + min: 1, + max: 4 + } + }, + { + item: Items.AMMO_44, + weight: 80, + spawnCount: { + min: 1, + max: 4 + } + }, + { + item: Items.AMMO_223, + weight: 50, + spawnCount: { + min: 1, + max: 5 + } + }, + { + item: Items.AMMO_762, + weight: 50, + spawnCount: { + min: 1, + max: 5 + } + }, + { + item: Items.AMMO_308, + weight: 50, + spawnCount: { + min: 1, + max: 4 + } + }, + { + item: Items.AMMO_12GA, + weight: 50, + spawnCount: { + min: 1, + max: 4 + } + }, + { + item: Items.WEAPON_1911, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_M9, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_R380, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_MAGNUM, + weight: 25, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_308, + weight: 13, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_SHOTGUN, + weight: 10, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_AR15, + weight: 10, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_AK47, + weight: 10, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + // #endregion + + // #region INDUSTRIAL + "ItemSpawnerIndustrial_Tier00.adr": { + spawnChance: 15, + items: [ + ...carparts, + { + item: Items.DUCT_TAPE, + weight: 20, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_CROWBAR, + weight: 25, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.TWINE, + weight: 20, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WATER_EMPTY, + weight: 15, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WOOD_PLANK, + weight: 50, + spawnCount: { + min: 1, + max: 5 + } + }, + { + item: Items.METAL_SHEET, + weight: 30, + spawnCount: { + min: 1, + max: 3 + } + }, + { + item: Items.METAL_SCRAP, + weight: 40, + spawnCount: { + min: 1, + max: 4 + } + }, + { + item: Items.METAL_PIPE, + weight: 20, + spawnCount: { + min: 1, + max: 2 + } + }, + { + item: Items.WEAPON_AXE_WOOD, + weight: 20, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.TARP, + weight: 40, + spawnCount: { + min: 1, + max: 2 + } + }, + { + item: Items.EMERGENCY_RADIO, + weight: 7, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + // #endregion + + // #region WORLD + "ItemSpawnerWorld_Tier00.adr": { + spawnChance: 20, + items: [ + { + item: Items.WEAPON_MACHETE01, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WATER_EMPTY, + weight: 15, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WATER_DIRTY, + weight: 10, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.SHIRT_DEFAULT, + weight: 40, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.PANTS_DEFAULT, + weight: 40, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CONVEYS_BLUE, + weight: 20, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.ZEDS_WHITE, + weight: 20, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.GATORS_RED, + weight: 20, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_HATCHET, + weight: 20, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.HAT_CAP, + weight: 40, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.HAT_BEANIE, + weight: 40, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.HELMET_MOTORCYCLE, + weight: 10, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD01, + weight: 20, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.EMERGENCY_RADIO, + weight: 7, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + // #endregion + + // #region COMMERCIAL + "ItemSpawnerCommercial_Tier00.adr": { + spawnChance: 20, + items: [ + { + item: Items.DUCT_TAPE, + weight: 5, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.BATTERY, + weight: 5, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.SPARKPLUGS, + weight: 5, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WATER_EMPTY, + weight: 15, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WATER_STAGNANT, + weight: 10, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD01, + weight: 20, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.EMERGENCY_RADIO, + weight: 7, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.GROUND_COFFEE, + weight: 15, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.SUGAR, + weight: 10, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD26, + weight: 7, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + GasStation: { + spawnChance: 100, + items: [ + { + item: Items.FUEL_ETHANOL, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.FUEL_BIOFUEL, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + // #endregion + + // #region FARM + "ItemSpawnerFarm.adr": { + spawnChance: 40, + items: [ + { + item: Items.FERTILIZER, + weight: 23, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_AXE_WOOD, + weight: 5, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.SEED_CORN, + weight: 15, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.SEED_WHEAT, + weight: 15, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_HATCHET, + weight: 5, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WATER_EMPTY, + weight: 15, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + // #endregion + + // #region HOSPITAL + "ItemSpawnerHospital.adr": { + spawnChance: 20, + items: [ + { + item: Items.FIRST_AID, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.MRE_APPLE, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.BANDAGE, + weight: 100, + spawnCount: { + min: 1, + max: 2 + } + }, + { + item: Items.VIAL_EMPTY, + weight: 100, + spawnCount: { + min: 1, + max: 2 + } + }, + { + item: Items.SYRINGE_EMPTY, + weight: 100, + spawnCount: { + min: 1, + max: 2 + } + }, + { + item: Items.SHIRT_SCRUBS_GRAY, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.PANTS_SCRUBS_GRAY, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CAP_SCRUBS_GRAY, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WATER_STAGNANT, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WATER_EMPTY, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.COLD_MEDICINE, + weight: 25, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CLOTH, + weight: 100, + spawnCount: { + min: 2, + max: 5 + } + } + ] + }, + // #endregion + + // #region MILITARY + "ItemSpawner_Z1_MilitaryBase_MotorPool.adr": { + spawnChance: 50, + items: [ + // COMMON + { + item: Items.DUCT_TAPE, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_BINOCULARS, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_COMBATKNIFE, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.FLARE, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.METAL_SCRAP, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.LANDMINE, + weight: 50, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_FLASHLIGHT, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.TARP, + weight: 100, + spawnCount: { + min: 1, + max: 2 + } + }, + { + item: Items.QUEST_MILITARY_SCRUBS_SHIRT, + weight: 5, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.QUEST_MILITARY_SCRUBS_PANTS, + weight: 5, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.QUEST_MILITARY_SCRUBS_CAP, + weight: 5, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.MRE_APPLE, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.BACKPACK_MILITARY_TAN, + weight: 10, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.EMERGENCY_RADIO, + weight: 50, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.GRENADE_SMOKE, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.GRENADE_FLASH, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_MOLOTOV, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_Z1_MilitaryBase_Tents1.adr": { + spawnChance: 50, + items: [ + // UNCOMON + { + item: Items.WEAPON_CROSSBOW, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_R380, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.QUEST_MILITARY_SCRUBS_SHIRT, + weight: 10, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.QUEST_MILITARY_SCRUBS_PANTS, + weight: 10, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.QUEST_MILITARY_SCRUBS_CAP, + weight: 10, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.GHILLIE_SUIT, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.HELMET_MOTORCYCLE, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.HELMET_TACTICAL, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.RESPIRATOR, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.FIRST_AID, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.AMMO_45, + weight: 100, + spawnCount: { + min: 1, + max: 5 + } + }, + { + item: Items.AMMO_9MM, + weight: 100, + spawnCount: { + min: 1, + max: 5 + } + }, + { + item: Items.AMMO_380, + weight: 100, + spawnCount: { + min: 1, + max: 5 + } + }, + { + item: Items.AMMO_44, + weight: 100, + spawnCount: { + min: 1, + max: 5 + } + }, + { + item: Items.AMMO_223, + weight: 100, + spawnCount: { + min: 1, + max: 5 + } + }, + { + item: Items.AMMO_762, + weight: 100, + spawnCount: { + min: 1, + max: 5 + } + }, + { + item: Items.AMMO_308, + weight: 100, + spawnCount: { + min: 1, + max: 4 + } + }, + { + item: Items.AMMO_12GA, + weight: 100, + spawnCount: { + min: 1, + max: 4 + } + }, + { + item: Items.NV_GOGGLES, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.MRE_APPLE, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.BACKPACK_MILITARY_TAN, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.EMERGENCY_RADIO, + weight: 50, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.GRENADE_SMOKE, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.GRENADE_FLASH, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_308, + weight: 50, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_SHOTGUN, + weight: 50, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_MOLOTOV, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_Z1_MilitaryBase_Tents2.adr": { + spawnChance: 10, + items: [ + // RARE + { + item: Items.WEAPON_MOLOTOV, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_MAGNUM, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.AMMO_308, + weight: 100, + spawnCount: { + min: 1, + max: 4 + } + }, + { + item: Items.AMMO_12GA, + weight: 100, + spawnCount: { + min: 1, + max: 4 + } + }, + { + item: Items.GUNPOWDER, + weight: 120, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.LANDMINE, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.KEVLAR_DEFAULT, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.EMERGENCY_RADIO, + weight: 50, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.GRENADE_SMOKE, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.QUEST_MILITARY_SCRUBS_SHIRT, + weight: 10, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.QUEST_MILITARY_SCRUBS_PANTS, + weight: 10, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.QUEST_MILITARY_SCRUBS_CAP, + weight: 10, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.GRENADE_FLASH, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_MOLOTOV, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_Z1_MilitaryBase_Hangar.adr": { + spawnChance: 20, + items: [ + // INDUSTRIAL + ...carparts, + { + item: Items.DUCT_TAPE, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.METAL_SHEET, + weight: 100, + spawnCount: { + min: 1, + max: 3 + } + }, + { + item: Items.METAL_SCRAP, + weight: 100, + spawnCount: { + min: 1, + max: 4 + } + }, + { + item: Items.WEAPON_PIPE, + weight: 100, + spawnCount: { + min: 1, + max: 2 + } + }, + { + item: Items.WEAPON_CROWBAR, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_HAMMER, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.FUEL_BIOFUEL, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.LANDMINE, + weight: 50, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_WRENCH, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.GRENADE_SMOKE, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.GRENADE_FLASH, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_MOLOTOV, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_Weapon_GrenadeSmoke.adr": { + spawnChance: 20, + items: [ + { + item: Items.GRENADE_SMOKE, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_Weapon_GrenadeFlashbang.adr": { + spawnChance: 20, + items: [ + { + item: Items.GRENADE_FLASH, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_Weapon_GrenadeGas.adr": { + spawnChance: 20, + items: [ + { + item: Items.GRENADE_GAS, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_Weapon_GrenadeHE.adr": { + spawnChance: 20, + items: [ + { + item: Items.GRENADE_HE, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + // #endregion + + // #region MISC + "ItemSpawner_BackpackOnGround001.adr": { + spawnChance: 8, + items: [ + { + item: Items.BACKPACK_BLUE_ORANGE, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.BACKPACK_MILITARY_TAN, + weight: 5, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_GasCan01.adr": { + spawnChance: 25, + items: [ + { + item: Items.FUEL_BIOFUEL, + weight: 100, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "ItemSpawner_Log01.adr": { + spawnChance: 50, + items: [ + { + item: Items.WOOD_LOG, + weight: 100, + spawnCount: { + min: 1, + max: 4 + } + } + ] + } + // #endregion +}; + +export const containerLootSpawners: { + [lootSpawner: string]: ContainerLootSpawner; +} = { + // TODO WHEN CONTAINERS WORK + "Wrecked Car": { + spawnChance: 100, + maxItems: 3, // cant be higher than length of items array below + items: [ + { + item: Items.METAL_SCRAP, + weight: 33, + spawnCount: { + min: 1, + max: 3 + } + }, + { + item: Items.METAL_SHEET, + weight: 33, + spawnCount: { + min: 1, + max: 3 + } + }, + { + item: Items.GUN_REPAIR_KIT, + weight: 15, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "Wrecked Van": { + spawnChance: 100, + maxItems: 3, + items: [ + { + item: Items.METAL_SCRAP, + weight: 33, + spawnCount: { + min: 1, + max: 3 + } + }, + { + item: Items.METAL_SHEET, + weight: 33, + spawnCount: { + min: 1, + max: 3 + } + }, + { + item: Items.WEAPON_REPAIR_KIT, + weight: 15, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "Wrecked Truck": { + spawnChance: 100, + maxItems: 4, + items: [ + { + item: Items.METAL_SCRAP, + weight: 33, + spawnCount: { + min: 1, + max: 3 + } + }, + { + item: Items.METAL_SHEET, + weight: 33, + spawnCount: { + min: 1, + max: 3 + } + }, + { + item: Items.TARP, + weight: 50, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.METAL_PIPE, + weight: 15, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "Weapons Locker": { + spawnChance: 60, + maxItems: 1, + items: [ + { + item: Items.WEAPON_AR15, + weight: 20, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.BACKPACK_MILITARY_TAN, + weight: 25, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.KEVLAR_DEFAULT, + weight: 35, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + Locker: { + spawnChance: 60, + maxItems: 1, + items: [ + { + item: Items.BACKPACK_MILITARY_TAN, + weight: 25, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.BACKPACK_BLUE_ORANGE, + weight: 10, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.KEVLAR_DEFAULT, + weight: 25, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.DEFAULT_TACTICAL_HELMET, + weight: 25, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + Desk: { + spawnChance: 100, + maxItems: 2, + items: [ + { + item: Items.CLOTH, + weight: 50, + spawnCount: { + min: 1, + max: 2 + } + }, + { + item: Items.HAT_CAP, + weight: 50, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.COMPASS, + weight: 5, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.VEHICLE_KEY, + weight: 25, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + Cabinets: { + spawnChance: 100, + maxItems: 2, + items: [ + { + item: Items.CLOTH, + weight: 15, + spawnCount: { + min: 1, + max: 2 + } + }, + { + item: Items.LIGHTER, + weight: 10, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.AMMO_45, + weight: 15, + spawnCount: { + min: 1, + max: 2 + } + }, + { + item: Items.SALT, + weight: 13, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD01, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD02, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD03, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD04, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD05, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD06, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD07, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD08, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD09, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD10, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD11, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD21, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD25, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD26, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.VEHICLE_KEY, + weight: 5, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "Cabinets Cube": { + spawnChance: 80, + maxItems: 1, + items: [ + { + item: Items.SALT, + weight: 13, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.LIGHTER, + weight: 5, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.VEHICLE_KEY, + weight: 5, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD01, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD02, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "Cabinets Kitchen": { + spawnChance: 100, + maxItems: 3, + items: [ + { + item: Items.SALT, + weight: 15, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.SUGAR, + weight: 10, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.GROUND_COFFEE, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.SEED_CORN, + weight: 25, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.SEED_WHEAT, + weight: 25, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD01, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD02, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD03, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD04, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD05, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD06, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD07, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD08, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD09, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD10, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD11, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD21, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD25, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD26, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "Cabinets Bathroom": { + spawnChance: 100, + maxItems: 3, + items: [ + { + item: Items.AIO_COLD_MEDICINE, + weight: 45, + spawnCount: { + min: 1, + max: 2 + } + }, + { + item: Items.ANTIBIOTICS, + weight: 45, + spawnCount: { + min: 1, + max: 2 + } + }, + { + item: Items.IMMUNITY_BOOSTERS, + weight: 45, + spawnCount: { + min: 1, + max: 2 + } + }, + { + item: Items.VITAMINS, + weight: 45, + spawnCount: { + min: 1, + max: 2 + } + }, + { + item: Items.BANDAGE, + weight: 25, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.FIRST_AID, + weight: 5, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "Tool Cabinet": { + spawnChance: 100, + maxItems: 3, + items: [ + { + item: Items.WEAPON_CROWBAR, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.METAL_PIPE, + weight: 37, + spawnCount: { + min: 1, + max: 2 + } + }, + { + item: Items.WEAPON_PIPE, + weight: 32, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.LIGHTER, + weight: 8, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_WRENCH, + weight: 35, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_HAMMER_DEMOLITION, + weight: 25, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "Drug Cabinets": { + spawnChance: 100, + maxItems: 2, + items: [ + { + item: Items.FIRST_AID, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.GAUZE, + weight: 30, + spawnCount: { + min: 1, + max: 3 + } + }, + { + item: Items.BANDAGE_DRESSED, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.BANDAGE, + weight: 30, + spawnCount: { + min: 1, + max: 2 + } + }, + { + item: Items.SALINE, + weight: 20, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.SYRINGE_EMPTY, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.VITAMINS, + weight: 30, + spawnCount: { + min: 1, + max: 2 + } + }, + { + item: Items.ANTIBIOTICS, + weight: 30, + spawnCount: { + min: 1, + max: 2 + } + }, + { + item: Items.EMPTY_SPECIMEN_BAG, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.VIAL_H1Z1_REDUCER, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.IMMUNITY_BOOSTERS, + weight: 30, + spawnCount: { + min: 1, + max: 2 + } + } + ] + }, + "Medical Station": { + spawnChance: 100, + maxItems: 2, + items: [ + { + item: Items.FIRST_AID, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.GAUZE, + weight: 30, + spawnCount: { + min: 1, + max: 3 + } + }, + { + item: Items.BANDAGE_DRESSED, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.BANDAGE, + weight: 30, + spawnCount: { + min: 1, + max: 2 + } + }, + { + item: Items.SALINE, + weight: 20, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.SYRINGE_EMPTY, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.VITAMINS, + weight: 30, + spawnCount: { + min: 1, + max: 2 + } + }, + { + item: Items.ANTIBIOTICS, + weight: 30, + spawnCount: { + min: 1, + max: 2 + } + }, + { + item: Items.EMPTY_SPECIMEN_BAG, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.VIAL_H1Z1_REDUCER, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.IMMUNITY_BOOSTERS, + weight: 30, + spawnCount: { + min: 1, + max: 2 + } + } + ] + }, + "Hospital Desk": { + spawnChance: 100, + maxItems: 3, + items: [ + { + item: Items.EMPTY_SPECIMEN_BAG, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.VIAL_H1Z1_REDUCER, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.GAUZE, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.BANDAGE_DRESSED, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.BANDAGE, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.SYRINGE_EMPTY, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.VITAMINS, + weight: 30, + spawnCount: { + min: 1, + max: 2 + } + }, + { + item: Items.ANTIBIOTICS, + weight: 30, + spawnCount: { + min: 1, + max: 2 + } + }, + { + item: Items.IMMUNITY_BOOSTERS, + weight: 30, + spawnCount: { + min: 1, + max: 2 + } + }, + { + item: Items.CRACKED_CLIPBOARD, + weight: 25, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.DEAD_CELL_PHONE, + weight: 23, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.DOCTORS_FILE, + weight: 23, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "Hospital Cabinets": { + spawnChance: 100, + maxItems: 3, + items: [ + { + item: Items.GAUZE, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.DOCTORS_MEMO, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAK_CELL_PHONE_BATTERY, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEICHS_WALLET, + weight: 14, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEICHS_REPORT, + weight: 14, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.KLAVISK_NOTE, + weight: 14, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.BANDAGE_DRESSED, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.BANDAGE, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.SYRINGE_EMPTY, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.EMPTY_SPECIMEN_BAG, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.VIAL_H1Z1_REDUCER, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.VITAMINS, + weight: 30, + spawnCount: { + min: 1, + max: 3 + } + }, + { + item: Items.ANTI_VIRAL_BOTTLE_EMPTY, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "Hospital Refrigerator": { + spawnChance: 100, + maxItems: 2, + items: [ + { + item: Items.SYRINGE_H1Z1_REDUCER, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.REFRIGERATOR_NOTE, + weight: 20, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.SYRINGE_INFECTED_BLOOD, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + "Grossing Station": { + spawnChance: 100, + maxItems: 2, + items: [ + { + item: Items.SYRINGE_H1Z1_REDUCER, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.SYRINGE_INFECTED_BLOOD, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + Dumpster: { + spawnChance: 100, + maxItems: 3, + items: [ + { + item: Items.CLOTH, + weight: 50, + spawnCount: { + min: 1, + max: 3 + } + }, + { + item: Items.TWINE, + weight: 25, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.FERTILIZER, + weight: 13, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CHARCOAL, + weight: 25, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WATER_EMPTY, + weight: 25, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.ANIMAL_FAT, + weight: 25, + spawnCount: { + min: 1, + max: 2 + } + } + ] + }, + "Garbage Can": { + spawnChance: 100, + maxItems: 3, + items: [ + { + item: Items.CLOTH, + weight: 50, + spawnCount: { + min: 1, + max: 3 + } + }, + { + item: Items.TWINE, + weight: 25, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.FERTILIZER, + weight: 13, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CHARCOAL, + weight: 25, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WATER_EMPTY, + weight: 25, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.ANIMAL_FAT, + weight: 25, + spawnCount: { + min: 1, + max: 2 + } + } + ] + }, + "File Cabinet": { + spawnChance: 100, + maxItems: 5, + items: [ + { + item: Items.LIGHTER, + weight: 3, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.SUGAR, + weight: 8, + spawnCount: { + min: 1, + max: 2 + } + }, + { + item: Items.AMMO_380, + weight: 15, + spawnCount: { + min: 1, + max: 2 + } + }, + { + item: Items.AMMO_44, + weight: 15, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.LOCKER_KEY_F1, + weight: 4, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.LOCKER_KEY_F2, + weight: 4, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.LOCKER_KEY_F3, + weight: 4, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.LOCKER_KEY_F4, + weight: 4, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + Fridge: { + spawnChance: 100, + maxItems: 2, + items: [ + { + item: Items.CANNED_FOOD01, + weight: 50, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.REFRIGERATOR_NOTE, + weight: 15, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.MEAT_VENISON, + weight: 25, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WATER_STAGNANT, + weight: 20, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.MEAT_ROTTEN, + weight: 25, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + Ottoman: { + spawnChance: 100, + maxItems: 3, + items: [ + { + item: Items.SHIRT_DEFAULT, + weight: 50, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.PANTS_DEFAULT, + weight: 50, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CLOTH, + weight: 50, + spawnCount: { + min: 1, + max: 4 + } + }, + { + item: Items.HAT_CAP, + weight: 50, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_FLASHLIGHT, + weight: 50, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.TWINE, + weight: 50, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.VEHICLE_KEY, + weight: 25, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.SILVER_METAL_FRAME_SUNGLASSES, + weight: 50, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + Dresser: { + spawnChance: 100, + maxItems: 3, + items: [ + { + item: Items.SHIRT_DEFAULT, + weight: 50, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.PANTS_DEFAULT, + weight: 50, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CLOTH, + weight: 50, + spawnCount: { + min: 1, + max: 4 + } + }, + { + item: Items.HAT_CAP, + weight: 50, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.TWINE, + weight: 50, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.VEHICLE_KEY, + weight: 20, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.COMPASS, + weight: 5, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.SILVER_METAL_FRAME_SUNGLASSES, + weight: 50, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + Armoire: { + spawnChance: 100, + maxItems: 3, + items: [ + { + item: Items.SHIRT_DEFAULT, + weight: 50, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.PANTS_DEFAULT, + weight: 50, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CLOTH, + weight: 50, + spawnCount: { + min: 1, + max: 4 + } + }, + { + item: Items.HAT_CAP, + weight: 50, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.TWINE, + weight: 50, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WAIST_PACK, + weight: 60, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.SILVER_METAL_FRAME_SUNGLASSES, + weight: 60, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + Washer: { + spawnChance: 10, + maxItems: 2, + items: [ + { + item: Items.SHIRT_DEFAULT, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.PANTS_DEFAULT, + weight: 20, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.BASIC_HOODIE, + weight: 10, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CLOTH, + weight: 5, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + Dryer: { + spawnChance: 10, + maxItems: 2, + items: [ + { + item: Items.SHIRT_DEFAULT, + weight: 30, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.PANTS_DEFAULT, + weight: 20, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.BASIC_HOODIE, + weight: 10, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CLOTH, + weight: 5, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + // used for crate props + Crate_buffed: { + spawnChance: 30, + maxItems: 1, + items: [ + { + item: Items.FERTILIZER, + weight: 25, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CLOTH, + weight: 15, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CANNED_FOOD01, + weight: 10, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WATER_DIRTY, + weight: 10, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + Crate: { + spawnChance: 50, + maxItems: 1, + items: [ + { + item: Items.CANNED_FOOD01, + weight: 20, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WATER_DIRTY, + weight: 20, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.CLOTH, + weight: 20, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.FERTILIZER, + weight: 6, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + + // airdrops + Farmer: { + spawnChance: 100, + maxItems: 1, + items: [ + { + item: Items.GROUND_TILLER, + weight: 1, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.GROUND_TILLER, + weight: 1, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.GROUND_TILLER, + weight: 1, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.GROUND_TILLER, + weight: 1, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.GROUND_TAMPER, + weight: 1, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.SEED_CORN, + weight: 1, + spawnCount: { + min: 10, + max: 10 + } + }, + { + item: Items.SEED_WHEAT, + weight: 1, + spawnCount: { + min: 10, + max: 10 + } + }, + { + item: Items.FERTILIZER, + weight: 1, + spawnCount: { + min: 10, + max: 15 + } + }, + { + item: Items.COMPASS, + weight: 1, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + Demolitioner: { + spawnChance: 100, + maxItems: 1, + items: [ + { + item: Items.WEAPON_HAMMER_DEMOLITION, + weight: 1, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.IED, + weight: 1, + spawnCount: { + min: 10, + max: 20 + } + }, + { + item: Items.FUEL_ETHANOL, + weight: 1, + spawnCount: { + min: 10, + max: 20 + } + }, + { + item: Items.LIGHTER, + weight: 1, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.COMPASS, + weight: 1, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + Medic: { + spawnChance: 100, + maxItems: 1, + items: [ + { + item: Items.GAUZE, + weight: 1, + spawnCount: { + min: 25, + max: 25 + } + }, + { + item: Items.BANDAGE_DRESSED, + weight: 1, + spawnCount: { + min: 15, + max: 15 + } + }, + { + item: Items.FIRST_AID, + weight: 1, + spawnCount: { + min: 10, + max: 10 + } + }, + { + item: Items.LOCKER_KEY_F1, + weight: 1, + spawnCount: { + min: 5, + max: 5 + } + }, + { + item: Items.LOCKER_KEY_F2, + weight: 1, + spawnCount: { + min: 5, + max: 5 + } + }, + { + item: Items.LOCKER_KEY_F3, + weight: 1, + spawnCount: { + min: 5, + max: 5 + } + }, + { + item: Items.LOCKER_KEY_F4, + weight: 1, + spawnCount: { + min: 5, + max: 5 + } + }, + { + item: Items.COMPASS, + weight: 1, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + Builder: { + spawnChance: 100, + maxItems: 1, + items: [ + { + item: Items.METAL_SCRAP, + weight: 1, + spawnCount: { + min: 50, + max: 50 + } + }, + { + item: Items.WOOD_LOG, + weight: 1, + spawnCount: { + min: 50, + max: 50 + } + }, + { + item: Items.WEAPON_AXE_WOOD, + weight: 1, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_CROWBAR, + weight: 1, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_HAMMER, + weight: 1, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.SHACK, + weight: 1, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WORKBENCH, + weight: 1, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.COMPASS, + weight: 1, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + Fighter: { + spawnChance: 100, + maxItems: 1, + items: [ + { + item: Items.WEAPON_SHOTGUN, + weight: 1, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.WEAPON_AK47, + weight: 1, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.AMMO_762, + weight: 1, + spawnCount: { + min: 50, + max: 50 + } + }, + { + item: Items.AMMO_12GA, + weight: 1, + spawnCount: { + min: 15, + max: 15 + } + }, + { + item: Items.COMPASS, + weight: 1, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + Supplier: { + spawnChance: 100, + maxItems: 1, + items: [ + { + item: Items.SANDWICH_DEER, + weight: 1, + spawnCount: { + min: 3, + max: 3 + } + }, + { + item: Items.SANDWICH_BEAR, + weight: 1, + spawnCount: { + min: 3, + max: 3 + } + }, + { + item: Items.SANDWICH_RABBIT, + weight: 1, + spawnCount: { + min: 3, + max: 3 + } + }, + { + item: Items.SANDWICH_WOLF, + weight: 1, + spawnCount: { + min: 3, + max: 3 + } + }, + { + item: Items.WATER_PURE, + weight: 1, + spawnCount: { + min: 15, + max: 15 + } + }, + { + item: Items.MOONSHINE, + weight: 1, + spawnCount: { + min: 10, + max: 10 + } + }, + { + item: Items.SKINNING_KNIFE, + weight: 1, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.COMPASS, + weight: 1, + spawnCount: { + min: 1, + max: 1 + } + } + ] + }, + Hospital: { + spawnChance: 0, + maxItems: 1, + items: [ + { + item: Items.BANDAGE, + weight: 1, + spawnCount: { + min: 1, + max: 1 + } + }, + { + item: Items.AMMO_45, + weight: 1, + spawnCount: { + min: 6, + max: 12 + } + }, + { + item: Items.AMMO_12GA, + weight: 1, + spawnCount: { + min: 4, + max: 8 + } + }, + { + item: Items.PAINKILLERS, + weight: 1, + spawnCount: { + min: 1, + max: 2 + } + }, + { + item: Items.ADRENALINE_SHOT, + weight: 1, + spawnCount: { + min: 1, + max: 2 + } + } + ] + } +}; diff --git a/src/servers/ZoneServer2016/entities/basefullcharacter.ts b/src/servers/ZoneServer2016/entities/basefullcharacter.ts index b5e6ff84d3..0eb97ec574 100644 --- a/src/servers/ZoneServer2016/entities/basefullcharacter.ts +++ b/src/servers/ZoneServer2016/entities/basefullcharacter.ts @@ -43,20 +43,13 @@ import { LOADOUT_CONTAINER_ID } from "../../../utils/constants"; import { DeathItemDamageConfig } from "../data/deathitemdamageconfig"; -import { PluginManager } from "../managers/pluginmanager"; const debugName = "ZoneServer", debug = require("debug")(debugName); -const loadoutSlots = PluginManager.loadServerData( - "2016/dataSources/LoadoutSlots.json" - ), - loadoutSlotItemClasses = PluginManager.loadServerData( - "2016/dataSources/LoadoutSlotItemClasses.json" - ), - equipSlotItemClasses = PluginManager.loadServerData( - "2016/dataSources/EquipSlotItemClasses.json" - ); +const loadoutSlots = require("./../../../../data/2016/dataSources/LoadoutSlots.json"), + loadoutSlotItemClasses = require("./../../../../data/2016/dataSources/LoadoutSlotItemClasses.json"), + equipSlotItemClasses = require("./../../../../data/2016/dataSources/EquipSlotItemClasses.json"); function getGender(actorModelId: number): number { switch (actorModelId) { @@ -644,13 +637,6 @@ export abstract class BaseFullCharacter extends BaseLightweightCharacter { availableContainer = this.getAvailableContainer(server, itemDefId, count); if (!availableContainer) { - // container error full - if (client) { - server.sendData(client, "Character.NoSpaceNotification", { - characterId: client.character.characterId - }); - } - this.getSortedContainers().forEach((c) => { if (item.stackCount <= 0) return; if (array.includes(c)) return; @@ -673,12 +659,46 @@ export abstract class BaseFullCharacter extends BaseLightweightCharacter { } }); if (item.stackCount > 0) { - server.worldObjectManager.createLootEntity( - server, - item, - this.state.position, - new Float32Array([0, Number(Math.random() * 10 - 5), 0, 1]) - ); + if (client?.character.mountedContainer) { + const mountedContainer = + client.character.mountedContainer.getContainer(); + if (mountedContainer) { + const itemStack = mountedContainer.getAvailableItemStack( + server, + item.itemDefinitionId, + item.stackCount + ); + if (itemStack) { + const targetItem = mountedContainer.items[itemStack]; + targetItem.stackCount += item.stackCount; + server.updateContainerItem( + client.character.mountedContainer, + targetItem, + mountedContainer + ); + } else + server.addContainerItem( + client.character.mountedContainer, + item, + mountedContainer, + true + ); + } + } else { + // container error full + if (client) { + server.sendData(client, "Character.NoSpaceNotification", { + characterId: client.character.characterId + }); + } + + server.worldObjectManager.createLootEntity( + server, + item, + this.state.position, + new Float32Array([0, Number(Math.random() * 10 - 5), 0, 1]) + ); + } } return; } diff --git a/src/servers/ZoneServer2016/entities/baselightweightcharacter.ts b/src/servers/ZoneServer2016/entities/baselightweightcharacter.ts index 61c0fbc76a..763516cf5a 100644 --- a/src/servers/ZoneServer2016/entities/baselightweightcharacter.ts +++ b/src/servers/ZoneServer2016/entities/baselightweightcharacter.ts @@ -154,7 +154,12 @@ export abstract class BaseLightweightCharacter extends BaseEntity { actorModelId: this.temporaryActorModelId ? this.temporaryActorModelId : this.actorModelId, - position: this.state.position, + // fix players / vehicles spawning in ground + position: new Float32Array( + Array.from(this.state.position).map((pos, idx) => { + return idx == 1 ? pos++ : pos; + }) + ), rotation: this.state.rotation, scale: this.scale, positionUpdateType: this.positionUpdateType, diff --git a/src/servers/ZoneServer2016/entities/character.ts b/src/servers/ZoneServer2016/entities/character.ts index 63680496ba..7430e5d8e1 100644 --- a/src/servers/ZoneServer2016/entities/character.ts +++ b/src/servers/ZoneServer2016/entities/character.ts @@ -80,8 +80,7 @@ import { BaseEntity } from "./baseentity"; import { ProjectileEntity } from "./projectileentity"; import { ChallengeType } from "../managers/challengemanager"; import { LootableConstructionEntity } from "./lootableconstructionentity"; -import { PluginManager } from "../managers/pluginmanager"; -const stats = PluginManager.loadServerData("2016/sampleData/stats.json"); +const stats = require("../../../../data/2016/sampleData/stats.json"); interface CharacterStates { invincibility: boolean; diff --git a/src/servers/ZoneServer2016/entities/constructionchildentity.ts b/src/servers/ZoneServer2016/entities/constructionchildentity.ts index 6a6eed32b2..3bc99ef5dc 100644 --- a/src/servers/ZoneServer2016/entities/constructionchildentity.ts +++ b/src/servers/ZoneServer2016/entities/constructionchildentity.ts @@ -72,7 +72,8 @@ import { isInsideCube, isPosInRadius, movePoint, - registerConstructionSlots + registerConstructionSlots, + shouldHideHealthBar } from "../../../utils/utils"; import { ZoneClient2016 } from "../classes/zoneclient"; import { ConstructionParentEntity } from "./constructionparententity"; @@ -532,12 +533,18 @@ export class ConstructionChildEntity extends BaseLightweightCharacter { } this.health -= damageInfo.damage; - server.sendDataToAllWithSpawnedEntity( - dictionary, - this.characterId, - "Character.UpdateSimpleProxyHealth", - this.pGetSimpleProxyHealth() - ); + for (const a in server._clients) { + const client = server._clients[a]; + if (client.spawnedEntities.has(dictionary[this.characterId])) { + server.sendData( + client, + "Character.UpdateSimpleProxyHealth", + shouldHideHealthBar(server, client, this) + ? 100 + : this.pGetSimpleProxyHealth() + ); + } + } const hasPerms = this.getHasPermission( server, diff --git a/src/servers/ZoneServer2016/entities/constructiondoor.ts b/src/servers/ZoneServer2016/entities/constructiondoor.ts index 0c2b4dc469..f77d1c84dc 100644 --- a/src/servers/ZoneServer2016/entities/constructiondoor.ts +++ b/src/servers/ZoneServer2016/entities/constructiondoor.ts @@ -38,7 +38,7 @@ function getDamageRange(definitionId: number): number { case Items.DOOR_WOOD: case Items.DOOR_METAL: case Items.DOOR_BASIC: - return 4.3; + return 2.5; default: return 2; } diff --git a/src/servers/ZoneServer2016/entities/crate.ts b/src/servers/ZoneServer2016/entities/crate.ts index 4084b5e123..a14199cf31 100644 --- a/src/servers/ZoneServer2016/entities/crate.ts +++ b/src/servers/ZoneServer2016/entities/crate.ts @@ -13,7 +13,8 @@ import { ZoneServer2016 } from "../zoneserver"; import { ZoneClient2016 } from "../classes/zoneclient"; import { DamageInfo } from "../../../types/zoneserver"; -import { randomIntFromInterval } from "../../../utils/utils"; +import { randomIntFromInterval, isPosInRadius } from "../../../utils/utils"; +import { containerLootSpawners } from "../data/lootspawns"; import { getRandomItem } from "../managers/worldobjectmanager"; import { BaseSimpleNpc } from "./basesimplenpc"; import { Effects, Items, ModelIds } from "../models/enums"; @@ -35,6 +36,36 @@ export function getActorModelId(actorModel: string): number { } } +function isBuffedCrate(position: Float32Array): boolean { + const buffedPostions: [number, number, number, number][] = [ + [1814.5, 48.88, 224.07, 1], + [2043.44, 46.28, 423.75, 1], + [2216.36, 49.72, 772.95, 1], + [2652.26, 57.87, 848.76, 1], + [2303.37, 54.47, 1203.46, 1], + [2333.07, 54.47, 1801.1, 1], + [2601.42, 32.0, 2046.66, 1], + [-2813.46, 47.66, 2735.36, 1], + [-2399.8, 16.19, 1871.99, 1], + [-3005.66, 52.51, -2055.08, 1], + [418.6, 21.66, -723.23, 1], + [-1514.96, 354.0, -832.32, 1], + [-1781.56, 75.91, 1717.67, 1], + [-448.43, 71.63, 1440.45, 1], + [-65.6, 53.75, 847.88, 1], + [-66.63, 56.4, 775.5, 1], + [101.67, 34.26, 254.99, 1], + [1604.81, 47.82, -592.11, 1], + [1587.97, 57.38, -222.97, 1], + [-2063.19, 62.97, 2722.55, 1] + ]; + let result = false; + for (const a of buffedPostions) { + if (isPosInRadius(40, position, new Float32Array(a))) result = true; + } + return result; +} + export class Crate extends BaseSimpleNpc { spawnerId: number; requiredItemId: number = 0; @@ -43,6 +74,8 @@ export class Crate extends BaseSimpleNpc { health: number = this.maxHealth; spawnTimestamp: number = 0; destroyed: boolean = false; + /** Returns true if the crate is in the radius of a buffed position (hunter drive, car camps etc.) */ + isBuffed: boolean; /** Time (milliseconds) for the crate to respawn in the world */ respawnTime = 900000; // 15min respawn time constructor( @@ -60,29 +93,24 @@ export class Crate extends BaseSimpleNpc { this.spawnerId = zoneId; this.scale = scale; this.npcRenderDistance = renderDistance; + this.isBuffed = isBuffedCrate(this.state.position); } spawnLoot(server: ZoneServer2016) { // Don't generate loot if crate is already destroyed if (this.destroyed) return; - const containerTables = - server.worldObjectManager.lootTableManager.getContainerTables(); - const lootTable = containerTables["Crate"]; - if (!lootTable) return; - const chance = Math.floor(Math.random() * 100) + 1; - if (chance <= (lootTable.spawnChance ?? 100)) { - const allEntries = lootTable.pools - .flatMap((p) => p.entries) - .filter((e) => (e.type ?? "item") === "item" && e.item !== undefined); - const entry = getRandomItem(allEntries); - if (entry && entry.item !== undefined) { + const lootTable = this.isBuffed + ? containerLootSpawners["Crate_buffed"] + : containerLootSpawners["Crate"]; + const chance = Math.floor(Math.random() * 100) + 1; // temporary spawnchance + if (chance <= lootTable.spawnChance) { + const item = getRandomItem(lootTable.items); + if (item) { const spawnedItem = server.worldObjectManager.createLootEntity( server, server.generateItem( - entry.item, - entry.count - ? randomIntFromInterval(entry.count.min, entry.count.max) - : 1 + item.item, + randomIntFromInterval(item.spawnCount.min, item.spawnCount.max) ), new Float32Array([ this.state.position[0], diff --git a/src/servers/ZoneServer2016/entities/explosiveentity.ts b/src/servers/ZoneServer2016/entities/explosiveentity.ts index 082651e2dc..843ba97be8 100644 --- a/src/servers/ZoneServer2016/entities/explosiveentity.ts +++ b/src/servers/ZoneServer2016/entities/explosiveentity.ts @@ -12,7 +12,11 @@ // ====================================================================== import { DamageInfo } from "types/zoneserver"; -import { getDistance, randomIntFromInterval } from "../../../utils/utils"; +import { + getDistance, + isChristmasSeason, + randomIntFromInterval +} from "../../../utils/utils"; import { Effects, Items } from "../models/enums"; import { ZoneServer2016 } from "../zoneserver"; import { BaseLightweightCharacter } from "./baselightweightcharacter"; @@ -42,8 +46,6 @@ export class ExplosiveEntity extends BaseLightweightCharacter { isAwaitingExplosion: boolean = false; - isArmed: boolean = false; - constructor( characterId: string, transientId: number, @@ -104,14 +106,30 @@ export class ExplosiveEntity extends BaseLightweightCharacter { let client = this.server.getClientByCharId(characterId); if (!this.server._explosives[this.characterId] || this.detonated) return; this.detonated = true; - this.server.explosionManager.queueExplosion(this, client); + this.server.sendCompositeEffectToAllInRange( + 600, + "", + this.state.position, + Effects.PFX_Impact_Explosion_Landmine_Dirt_10m + ); + if (isChristmasSeason()) { + this.server.sendCompositeEffectToAllInRange( + 600, + "", + this.state.position, + Effects.PFX_Seasonal_Holiday_Snow_skel + ); + } + queueMicrotask(() => { + this.server.deleteEntity(this.characterId, this.server._explosives); + }); + this.server.explosionDamage(this, client); } /** Used by landmines to arm their explosivenss */ async arm(server: ZoneServer2016) { // Wait 10 seconds before activating the trap await scheduler.wait(10_000); - this.isArmed = true; server.aiManager.addEntity(this); } diff --git a/src/servers/ZoneServer2016/entities/lootableconstructionentity.ts b/src/servers/ZoneServer2016/entities/lootableconstructionentity.ts index c329ffa8e1..8e3e355a7d 100644 --- a/src/servers/ZoneServer2016/entities/lootableconstructionentity.ts +++ b/src/servers/ZoneServer2016/entities/lootableconstructionentity.ts @@ -31,7 +31,14 @@ import { EXTERNAL_CONTAINER_GUID } from "../../../utils/constants"; import { CharacterPlayWorldCompositeEffect } from "types/zone2016packets"; import { scheduler } from "timers/promises"; import { BaseEntity } from "./baseentity"; -import { isPosInRadius } from "../../../utils/utils"; +import { + checkLineThroughDoorway, + isPosInRadius, + rotateAroundPivot, + shouldHideHealthBar, + wallInterceptsLine +} from "../../../utils/utils"; +import { ConstructionDoor } from "./constructiondoor"; function getMaxHealth(itemDefinitionId: Items): number { switch (itemDefinitionId) { @@ -119,12 +126,18 @@ export class LootableConstructionEntity extends BaseLootableEntity { } this.health -= damageInfo.damage; - server.sendDataToAllWithSpawnedEntity( - dictionary, - this.characterId, - "Character.UpdateSimpleProxyHealth", - this.pGetSimpleProxyHealth() - ); + for (const a in server._clients) { + const client = server._clients[a]; + if (client.spawnedEntities.has(dictionary[this.characterId])) { + server.sendData( + client, + "Character.UpdateSimpleProxyHealth", + shouldHideHealthBar(server, client, this) + ? 100 + : this.pGetSimpleProxyHealth() + ); + } + } if (this.health > 0) return; this.destroy(server, 3000); @@ -400,4 +413,94 @@ export class LootableConstructionEntity extends BaseLootableEntity { sourceEntity ); } + + checkBuildingObstruct( + server: ZoneServer2016, + character: Float32Array, + foundation: ConstructionParentEntity | undefined + ): boolean { + if (!foundation) return false; + + const charPos = new Float32Array(character); + + const containerPos = this.state.position; + charPos[1] += 1.8; + + const allShelters = { + ...foundation.occupiedShelterSlots, + ...Object.assign( + {}, + ...Object.values(foundation.occupiedExpansionSlots).map( + (exp) => exp.occupiedShelterSlots + ) + ) + }; + + const allWalls = { + ...foundation.occupiedWallSlots, + ...Object.assign( + {}, + ...Object.values(foundation.occupiedExpansionSlots).map( + (exp) => exp.occupiedWallSlots + ) + ) + }; + + for (const w in allWalls) { + const wall = allWalls[w]; + const wallStart = new Float32Array(wall.state.position); + let wallEnd = new Float32Array(wall.fixedPosition); + + wallEnd[0] = 2 * wallEnd[0] - wallStart[0]; // doorEnd is currently the midpoint + wallEnd[2] = 2 * wallEnd[2] - wallStart[2]; // extend it to the end + wallEnd[1] += 2; + + if (wall instanceof ConstructionDoor && wall.isOpen) + wallEnd = rotateAroundPivot(wallStart, wallEnd, -Math.PI / 2); + + if (wallInterceptsLine(charPos, containerPos, wallStart, wallEnd)) { + if ( + wall.itemDefinitionId == Items.METAL_DOORWAY && + checkLineThroughDoorway(charPos, containerPos, wall) + ) + continue; + else return true; + } + } + + for (const s in allShelters) { + const shelter: ConstructionChildEntity = allShelters[s]; + + if (!shelter.cubebounds) continue; + const walls: [Float32Array, Float32Array][] = [ + [ + new Float32Array(shelter.cubebounds[0]), + new Float32Array(shelter.cubebounds[5]) + ], + + [ + new Float32Array(shelter.cubebounds[1]), + new Float32Array(shelter.cubebounds[6]) + ], + + [ + new Float32Array(shelter.cubebounds[2]), + new Float32Array(shelter.cubebounds[7]) + ], + + [ + new Float32Array(shelter.cubebounds[3]), + new Float32Array(shelter.cubebounds[4]) + ] + ]; + + for (const wall of walls) { + if (wallInterceptsLine(charPos, containerPos, ...wall)) { + return !checkLineThroughDoorway(charPos, containerPos, shelter); + } + } + } + + return false; + } } diff --git a/src/servers/ZoneServer2016/entities/lootableprop.ts b/src/servers/ZoneServer2016/entities/lootableprop.ts index 26893f619b..0135ad1060 100644 --- a/src/servers/ZoneServer2016/entities/lootableprop.ts +++ b/src/servers/ZoneServer2016/entities/lootableprop.ts @@ -25,24 +25,24 @@ function getContainerAndTime(entity: LootableProp) { case ModelIds.WRECKED_VAN: entity.containerId = Items.CONTAINER_WRECKED_VAN; entity.searchTime = 1500; - entity.lootSpawner = "Wrecked_Van"; + entity.lootSpawner = "Wrecked Van"; break; case ModelIds.WRECKED_CAR: case ModelIds.WRECKED_CAR_OD_01: entity.containerId = Items.CONTAINER_WRECKED_CAR; entity.searchTime = 1500; - entity.lootSpawner = "Wrecked_Car"; + entity.lootSpawner = "Wrecked Car"; break; case ModelIds.WRECKED_TRUCK_01: case ModelIds.WRECKED_TRUCK_OD_01: entity.containerId = Items.CONTAINER_WRECKED_TRUCK; entity.searchTime = 1500; - entity.lootSpawner = "Wrecked_Truck"; + entity.lootSpawner = "Wrecked Truck"; break; case ModelIds.LOCKERS_LOCKER_WEAPONS: entity.containerId = Items.CONTAINER_WEAPONS_LOCKER; entity.searchTime = 500; - entity.lootSpawner = "Weapons_Locker"; + entity.lootSpawner = "Weapons Locker"; break; case ModelIds.LOCKERS_LOCKER_POLICE: entity.containerId = Items.CONTAINER_LOCKER; @@ -63,7 +63,7 @@ function getContainerAndTime(entity: LootableProp) { case ModelIds.OFFICE_CUBE_CABINET: entity.containerId = Items.CONTAINER_CABINETS_CUBE; entity.searchTime = 500; - entity.lootSpawner = "Cabinets_Cube"; + entity.lootSpawner = "Cabinets Cube"; break; case ModelIds.CABINET_SET_03: @@ -74,7 +74,7 @@ function getContainerAndTime(entity: LootableProp) { case ModelIds.CABINET_SET_09: entity.containerId = Items.CONTAINER_CABINETS_KITCHEN; entity.searchTime = 5000; - entity.lootSpawner = "Cabinets_Kitchen"; + entity.lootSpawner = "Cabinets Kitchen"; break; case ModelIds.CABINETS_KITCHEN_02: case ModelIds.CABINETS_KITCHEN_03: @@ -89,14 +89,14 @@ function getContainerAndTime(entity: LootableProp) { case ModelIds.CABINET_SET_11: entity.containerId = Items.CONTAINER_CABINETS_KITCHEN; entity.searchTime = 1000; - entity.lootSpawner = "Cabinets_Kitchen"; + entity.lootSpawner = "Cabinets Kitchen"; break; case ModelIds.CABINETS_BATHROOM_01: case ModelIds.CABINETS_BATHROOM_SINK: case ModelIds.CABINETS_BATHROOM_02: entity.containerId = Items.CONTAINER_CABINETS_BATHROOM; entity.searchTime = 500; - entity.lootSpawner = "Cabinets_Bathroom"; + entity.lootSpawner = "Cabinets Bathroom"; break; case ModelIds.BLUE_TOOL_CABINET: case ModelIds.RED_SILVER_TOOL_CABINET: @@ -104,7 +104,7 @@ function getContainerAndTime(entity: LootableProp) { case ModelIds.TOOL_CABINET_02: entity.containerId = Items.CONTAINER_TOOL_CABINETS; entity.searchTime = 500; - entity.lootSpawner = "Tool_Cabinet"; + entity.lootSpawner = "Tool Cabinet"; break; case ModelIds.DUMPSTER: entity.containerId = Items.CONTAINER_DUMPSTER; @@ -117,7 +117,7 @@ function getContainerAndTime(entity: LootableProp) { case ModelIds.FILE_CABINETS: entity.containerId = Items.CONTAINER_FILE_CABINET; entity.searchTime = 500; - entity.lootSpawner = "File_Cabinet"; + entity.lootSpawner = "File Cabinet"; break; case ModelIds.KITCHEN_FRIDGE: entity.containerId = Items.CONTAINER_FRIDGE; @@ -155,38 +155,38 @@ function getContainerAndTime(entity: LootableProp) { case ModelIds.GARBAGE_CAN_01: entity.containerId = Items.CONTAINER_GARBAGE_CAN; entity.searchTime = 500; - entity.lootSpawner = "Garbage_Can"; + entity.lootSpawner = "Garbage Can"; break; case ModelIds.HOSPITAL_DRUG_CABNINET: entity.containerId = Items.CONTAINER_DRUG_CABINET; entity.searchTime = 1000; - entity.lootSpawner = "Drug_Cabinets"; + entity.lootSpawner = "Drug Cabinets"; break; case ModelIds.HOSPITAL_LAB_WORKBENCH: entity.containerId = Items.CONTAINER_MEDICAL_STATION; entity.searchTime = 0; - entity.lootSpawner = "Medical_Station"; + entity.lootSpawner = "Medical Station"; break; case ModelIds.HOSPITAL_DESK: case ModelIds.HOSPITAL_LAB_COUNTERTOP: entity.containerId = Items.CONTAINER_HOSPITAL_DESK; entity.searchTime = 1000; - entity.lootSpawner = "Hospital_Desk"; + entity.lootSpawner = "Hospital Desk"; break; case ModelIds.HOSPITAL_GROSSING_STATION: entity.containerId = Items.CONTAINER_GROSSING_STATION; entity.searchTime = 1000; - entity.lootSpawner = "Grossing_Station"; + entity.lootSpawner = "Grossing Station"; break; case ModelIds.HOSPITAL_REFRIGERATOR: entity.containerId = Items.CONTAINER_HOSPITAL_REFRIGERATOR; entity.searchTime = 1000; - entity.lootSpawner = "Hospital_Refrigerator"; + entity.lootSpawner = "Hospital Refrigerator"; break; case ModelIds.HOSPITAL_SINK_COUNTERTOP: entity.containerId = Items.CONTAINER_HOSPITAL_CABINET; entity.searchTime = 1000; - entity.lootSpawner = "Hospital_Cabinets"; + entity.lootSpawner = "Hospital Cabinets"; break; case ModelIds.TREASURE_CHEST: entity.containerId = Items.CONTAINER_LOOT_CACHE; @@ -222,7 +222,7 @@ export class LootableProp extends BaseLootableEntity { containerId: number = Items.CONTAINER_STORAGE; /** Determines the loot table to distribute to the LootableProp */ - lootSpawner: string = "Wrecked_Car"; + lootSpawner: string = "Wrecked Car"; /** Time (milliseconds) it takes before the container loads for the player */ searchTime: number = 1000; @@ -244,9 +244,9 @@ export class LootableProp extends BaseLootableEntity { this.loadoutId = 5; getContainerAndTime(this); /*switch (this.lootSpawner) { - case "Wrecked_Van": - case "Wrecked_Car": - case "Wrecked_Truck": + case "Wrecked Van": + case "Wrecked Car": + case "Wrecked Truck": this.useSimpleStruct = false; this.state.rotation = eul2quat( new Float32Array([ @@ -338,9 +338,9 @@ export class LootableProp extends BaseLootableEntity { OnMeleeHit(server: ZoneServer2016, damageInfo: DamageInfo) { switch (this.lootSpawner) { - case "Wrecked_Van": - case "Wrecked_Car": - case "Wrecked_Truck": + case "Wrecked Van": + case "Wrecked Car": + case "Wrecked Truck": break; default: return; @@ -349,30 +349,7 @@ export class LootableProp extends BaseLootableEntity { const client = server.getClientByCharId(damageInfo.entity); const weapon = client?.character.getEquippedWeapon(); - if ( - !client || - !weapon || - weapon.itemDefinitionId != Items.WEAPON_CROWBAR || - client?.character.isHidden?.length > 0 || - client?.character.insideBuilding?.length > 0 - ) { - return; - } - // If scrap limiting is disabled then skip grid scanning entirely. - if (!server.worldObjectManager.gridScrapLimitEnabled) { - if (randomIntFromInterval(0, 100) <= server.crowbarHitRewardChance) { - client.character.lootItem( - server, - server.generateItem(Items.METAL_SCRAP) - ); - server.challengeManager.registerChallengeProgression( - client, - ChallengeType.RECYCLING, - 1 - ); - server.lootCrateWithChance(client, 2); - } - server.damageItem(client.character, weapon, server.crowbarHitDamage); + if (!client || !weapon || weapon.itemDefinitionId != Items.WEAPON_CROWBAR) { return; } for (let x = 0; x < server._grid.length; x++) { diff --git a/src/servers/ZoneServer2016/entities/plant.ts b/src/servers/ZoneServer2016/entities/plant.ts index a1ca4fc8ee..48472afe89 100644 --- a/src/servers/ZoneServer2016/entities/plant.ts +++ b/src/servers/ZoneServer2016/entities/plant.ts @@ -33,9 +33,6 @@ export class Plant extends ItemObject { /** Next time (milliseconds) that the crop will enter the next state */ nextStateTime: number; - /** Next time (milliseconds) that a fertilizer mound can appear */ - nextMoundTime: number = 0; - /** Time (milliseconds) it takes for a crop to enter the next state - Default: 8hrs */ readonly growTime = 28800000; @@ -81,10 +78,7 @@ export class Plant extends ItemObject { const parent = server._temporaryObjects[ parentObjectCharacterId ] as PlantingDiameter; - if (parent.isFertilized) { - this.isFertilized = true; - this.nextStateTime = new Date().getTime() + this.growTime / 2; - } + if (parent.isFertilized) this.isFertilized = true; if (this.item.itemDefinitionId == Items.SEED_CORN) { this.nameId = StringIds.CORN; } else this.nameId = StringIds.WHEAT; @@ -129,6 +123,21 @@ export class Plant extends ItemObject { modelId: this.actorModelId } ); + if (this.isFertilized) { + const pos = this.state.position; + server.sendDataToAllWithSpawnedEntity( + // play burning effect & remove it after 15s + server._plants, + this.characterId, + "Character.PlayWorldCompositeEffect", + { + characterId: this.characterId, + effectId: Effects.EFX_Crop_Fertilizer, + position: new Float32Array([pos[0], pos[1], pos[2], 1]), + effectTime: 180 + } + ); + } const timeToAdd = this.isFertilized ? this.growTime / 2 : this.growTime; // 4 or 8h based on fertilized or not this.nextStateTime = new Date().getTime() + timeToAdd; } @@ -163,7 +172,6 @@ export class Plant extends ItemObject { this.parentObjectCharacterId ] as PlantingDiameter; delete parent.seedSlots[this.slot]; - parent.disappearTimestamp = new Date().getTime() + 86400000; switch (this.item.itemDefinitionId) { case Items.SEED_WHEAT: @@ -192,20 +200,6 @@ export class Plant extends ItemObject { } OnInteractionString(server: ZoneServer2016, client: ZoneClient2016): void { - if (this.isFertilized && new Date().getTime() > this.nextMoundTime) { - const pos = this.state.position; - this.nextMoundTime = new Date().getTime() + 180000; - server.sendData( - client, - "Character.PlayWorldCompositeEffect", - { - characterId: this.characterId, - effectId: Effects.EFX_Crop_Fertilizer, - position: new Float32Array([pos[0], pos[1], pos[2], 1]), - effectTime: 180 - } - ); - } if (this.growState != 3) return; server.sendData(client, "Command.InteractionString", { guid: this.characterId, @@ -214,16 +208,14 @@ export class Plant extends ItemObject { } OnFullCharacterDataRequest( - _server: ZoneServer2016, - _client: ZoneClient2016 + server: ZoneServer2016, + client: ZoneClient2016 ): void { - /* if (!this.isFertilized) return; server.sendData(client, "Command.PlayDialogEffect", { characterId: this.characterId, effectId: Effects.EFX_Crop_Fertilizer }); - */ } destroy(server: ZoneServer2016): boolean { diff --git a/src/servers/ZoneServer2016/entities/projectileentity.ts b/src/servers/ZoneServer2016/entities/projectileentity.ts index 2009783ff4..d618bfad31 100644 --- a/src/servers/ZoneServer2016/entities/projectileentity.ts +++ b/src/servers/ZoneServer2016/entities/projectileentity.ts @@ -219,10 +219,9 @@ export class ProjectileEntity extends BaseLightweightCharacter { break; } - if (this.itemDefinitionId == Items.GRENADE_HE) - server.explosionManager.queueExplosion(this); + if (this.itemDefinitionId == Items.GRENADE_HE) server.explosionDamage(this); if (this.itemDefinitionId == Items.WEAPON_BOW_RECURVE) - server.explosionManager.queueExplosion(this); + server.explosionDamage(this); if (this.itemDefinitionId == Items.GRENADE_GAS) { if (server.isPvE) return; this.gasDamageInterval = setInterval(() => { diff --git a/src/servers/ZoneServer2016/entities/vehicle.ts b/src/servers/ZoneServer2016/entities/vehicle.ts index 2fa323ab18..f13bbbc1cc 100644 --- a/src/servers/ZoneServer2016/entities/vehicle.ts +++ b/src/servers/ZoneServer2016/entities/vehicle.ts @@ -1339,7 +1339,7 @@ export class Vehicle2016 extends BaseLootableEntity { ); const deleted = server.deleteEntity(this.characterId, server._vehicles); if (!disableExplosion) { - server.explosionManager.queueExplosion(this); + server.explosionDamage(this); } //this.state.position[1] -= 0.4; // makes bags spawn under the map sometimes. // TODO: Have to revisit when the heightmap is implemented server side. diff --git a/src/servers/ZoneServer2016/entities/watersource.ts b/src/servers/ZoneServer2016/entities/watersource.ts index 5037599691..8f2866a798 100644 --- a/src/servers/ZoneServer2016/entities/watersource.ts +++ b/src/servers/ZoneServer2016/entities/watersource.ts @@ -204,6 +204,7 @@ export class WaterSource extends TaskProp { weaponId = weapon?.itemDefinitionId, activatableItems = [ Items.WEAPON_WRENCH, + Items.WEAPON_BRANCH, Items.WEAPON_HAMMER, Items.WEAPON_HAMMER_DEMOLITION ]; diff --git a/src/servers/ZoneServer2016/handlers/commands/commands.ts b/src/servers/ZoneServer2016/handlers/commands/commands.ts index 33890266df..b938e6d9b4 100644 --- a/src/servers/ZoneServer2016/handlers/commands/commands.ts +++ b/src/servers/ZoneServer2016/handlers/commands/commands.ts @@ -37,8 +37,7 @@ import { characterTestKitLoadout, characterSkinsLoadout, characterKitLoadout, - characterVehicleKit, - characterFarmKitLoadout + characterVehicleKit } from "../../data/loadouts"; import { emoteMap, emoteNames, defaultEmotes } from "../../data/emotes"; import { @@ -66,10 +65,7 @@ import { scheduler } from "node:timers/promises"; import { Vehicle2016 } from "../../entities/vehicle"; import { AddSimpleNpc } from "types/zone2016packets"; import { writeFileSync } from "node:fs"; -import { PluginManager } from "../../managers/pluginmanager"; -const itemDefinitions = PluginManager.loadServerData( - "2016/dataSources/ServerItemDefinitions.json" -); +const itemDefinitions = require("./../../../../../data/2016/dataSources/ServerItemDefinitions.json"); export const commands: Array = [ //#region DEFAULT PERMISSIONS @@ -425,11 +421,11 @@ export const commands: Array = [ client: Client, args: Array ) => { - const stats = await server._gatewayServer.getSoeClientNetworkStats( + const stats = server._gatewayServer.getSoeClientNetworkStats( client.soeClientId ); if (stats) { - const serverStats = await server._gatewayServer.getServerNetworkStats(); + const serverStats = server._gatewayServer.getServerNetworkStats(); stats.push(serverStats[0]); for (let index = 0; index < stats.length; index++) { const stat = stats[index]; @@ -446,10 +442,10 @@ export const commands: Array = [ client: Client, args: Array ) => { - const stats = await server._gatewayServer.getSoeClientNetworkStats( + const stats = server._gatewayServer.getSoeClientNetworkStats( client.soeClientId ); - const serverStats = await server._gatewayServer.getServerNetworkStats(); + const serverStats = server._gatewayServer.getServerNetworkStats(); if (stats) { server.sendChatText(client, stats[2], true); server.sendChatText(client, serverStats[0], false); @@ -2059,7 +2055,7 @@ export const commands: Array = [ if (object.characterId == client.character.characterId) return; server.despawnEntity(object.characterId); }); - client.spawnedEntities.clear(); + client.spawnedEntities = new Set(); server._lootableProps = {}; server._npcs = {}; server._spawnedItems = {}; @@ -2793,13 +2789,6 @@ export const commands: Array = [ ); client.character.equipLoadout(server, characterBuildKitLoadout, true); break; - case "farm": - client.character.equipItem( - server, - server.generateItem(Items.FANNY_PACK_DEV) - ); - client.character.equipLoadout(server, characterFarmKitLoadout, true); - break; default: server.sendChatText( client, @@ -2844,24 +2833,16 @@ export const commands: Array = [ { name: "spawnloot", permissionLevel: PermissionLevels.ADMIN, - execute: async ( - server: ZoneServer2016, - client: Client, - args: Array - ) => { - await server.worldObjectManager.createLootThreaded(server); + execute: (server: ZoneServer2016, client: Client, args: Array) => { + server.worldObjectManager.createLoot(server); server.sendChatText(client, `Spawned loot`); } }, { name: "respawnnpcs", permissionLevel: PermissionLevels.ADMIN, - execute: async ( - server: ZoneServer2016, - client: Client, - args: Array - ) => { - await server.worldObjectManager.createNpcsThreaded(server); + execute: (server: ZoneServer2016, client: Client, args: Array) => { + server.worldObjectManager.createNpcs(server); server.sendChatText(client, `Respawned npcs`); } }, @@ -2938,54 +2919,6 @@ export const commands: Array = [ server.sendAlertToAll(args.join(" "), client.character.name); } }, - { - name: "globalalert", - permissionLevel: PermissionLevels.ADMIN, - keepCase: true, - execute: (server: ZoneServer2016, client: Client, args: Array) => { - if (!args.length) { - server.sendChatText(client, "[ERROR] Usage: /globalalert {message}"); - return; - } - const message = args.join(" "); - server.sendGlobalBroadcastRequest(0, client.character.name, message); - } - }, - { - name: "globalrewardtoall", - permissionLevel: PermissionLevels.ADMIN, - execute: (server: ZoneServer2016, client: Client, args: Array) => { - if (!args.length) { - server.sendChatText( - client, - "[ERROR] Usage: /globalrewardtoall {CrateID} [CrateID ...]" - ); - return; - } - const rewardIds: number[] = []; - const invalid: string[] = []; - for (const arg of args) { - const rewardId = Number(arg); - const validRewardItem = server.rewardManager.rewards.some( - (v) => v.itemId === rewardId - ); - if (!validRewardItem) { - invalid.push(arg); - continue; - } - rewardIds.push(rewardId); - } - if (!rewardIds.length) { - server.sendChatText( - client, - `[ERROR]${invalid.length ? " Crate ID: " + invalid.join(", ") : ""} is not valid` - ); - return; - } - const message = `${client.character.name} has just initiated a global crate drop`; - server.sendGlobalBroadcastRequest(1, "", message, rewardIds); - } - }, { name: "remover", permissionLevel: PermissionLevels.ADMIN, @@ -3551,9 +3484,10 @@ export const commands: Array = [ } } - await server.worldObjectManager.createLootThreaded(server); - await server.worldObjectManager.createContainerLootThreaded(server); - await new Promise((r) => setImmediate(r)); + delete require.cache[require.resolve("../../data/lootspawns")]; + const loottables = require("../../data/lootspawns").lootTables; + server.worldObjectManager.createLoot(server, loottables); + server.worldObjectManager.createContainerLoot(server); server.sendChatText(client, `Respawned loot`); } }, @@ -3657,22 +3591,6 @@ export const commands: Array = [ server.sendChatText(client, "Aborted server shutdown."); } }, - { - name: "reloadconfig", - permissionLevel: PermissionLevels.ADMIN, - execute: async ( - server: ZoneServer2016, - client: Client, - args: Array - ) => { - const success = server.configManager.reload(server); - if (success) { - server.sendChatText(client, "Config reloaded successfully."); - } else { - server.sendChatText(client, "Failed to reload config."); - } - } - }, { name: "console", permissionLevel: PermissionLevels.DEFAULT, diff --git a/src/servers/ZoneServer2016/handlers/commands/dev.ts b/src/servers/ZoneServer2016/handlers/commands/dev.ts index 10c4d90810..1b92fb07cf 100644 --- a/src/servers/ZoneServer2016/handlers/commands/dev.ts +++ b/src/servers/ZoneServer2016/handlers/commands/dev.ts @@ -50,17 +50,10 @@ import { scheduler } from "timers/promises"; import { DB_COLLECTIONS } from "../../../../utils/enums"; import { randomInt } from "crypto"; import { GatewayChannels } from "h1emu-core"; -import { PluginManager } from "../../managers/pluginmanager"; - -const abilities = PluginManager.loadServerData( - "2016/dataSources/Abilities.json" - ), - vehicleAbilities = PluginManager.loadServerData( - "2016/dataSources/VehicleAbilities.json" - ), - discovery = PluginManager.loadServerData( - "2016/dataSources/ClientDiscoveries.json" - ); + +const abilities = require("../../../../../data/2016/dataSources/Abilities.json"), + vehicleAbilities = require("../../../../../data/2016/dataSources/VehicleAbilities.json"), + discovery = require("../../../../../data/2016/dataSources/ClientDiscoveries.json"); const dev: any = { load_balancing: function ( @@ -68,13 +61,6 @@ const dev: any = { client: Client, args: Array ) { - if (!("_soeServer" in server._gatewayServer)) { - server.sendChatText( - client, - "load_balancing dev command requires non-threaded gateway internals" - ); - return; - } const nb = Number(args[1]) || 10; const basePort = 60_000; for (let index = 0; index < nb; index++) { @@ -389,14 +375,10 @@ const dev: any = { ] }); - const categories = PluginManager.loadServerData( - "2016/marketplaceData/categories.json" - ); + const categories = require("../../../../../data/2016/marketplaceData/categories.json"); server.sendData(client, "InGamePurchase.StoreBundleCategories", categories); - const bundles = PluginManager.loadServerData( - "2016/marketplaceData/bundles.json" - ); + const bundles = require("../../../../../data/2016/marketplaceData/bundles.json"); server.sendData(client, "InGamePurchase.StoreBundles", bundles); server.sendData(client, "InGamePurchase.SubscriptionProductsResponse", { @@ -948,7 +930,7 @@ const dev: any = { client: Client, args: Array ) { - const models = PluginManager.loadServerData("2016/dataSources/Models.json"); + const models = require("../../../../../data/2016/dataSources/Models.json"); const wordFilter = args[1]; if (wordFilter) { const result = models.filter((word: any) => diff --git a/src/servers/ZoneServer2016/managers/abilitiesmanager.ts b/src/servers/ZoneServer2016/managers/abilitiesmanager.ts index b8cd716384..51958a0c64 100644 --- a/src/servers/ZoneServer2016/managers/abilitiesmanager.ts +++ b/src/servers/ZoneServer2016/managers/abilitiesmanager.ts @@ -40,10 +40,7 @@ import { import { DamageInfo, EntityDictionary } from "types/zoneserver"; import { LoadoutItem } from "../classes/loadoutItem"; import { Npc } from "../entities/npc"; -import { PluginManager } from "./pluginmanager"; -const vehicleAbilities = PluginManager.loadServerData( - "2016/dataSources/VehicleAbilities.json" -); +const vehicleAbilities = require("../../../../data/2016/dataSources/VehicleAbilities.json"); export class AbilitiesManager { sendVehicleAbilities( diff --git a/src/servers/ZoneServer2016/managers/accountinventorymanager.ts b/src/servers/ZoneServer2016/managers/accountinventorymanager.ts index cc2d8af57d..541594c9b0 100644 --- a/src/servers/ZoneServer2016/managers/accountinventorymanager.ts +++ b/src/servers/ZoneServer2016/managers/accountinventorymanager.ts @@ -32,24 +32,21 @@ export class AccountInventoryManager { this.mongodbCollection = collection; } - private async _getSoloAccountItems( - loginSessionId: string - ): Promise { - const raw = await fs.promises.readFile(this._soloDataPath, "utf8"); - return (JSON.parse(raw) as any[]).filter( - (e) => e.loginSessionId === loginSessionId - ); + private _getSoloAccountItems(loginSessionId: string): AccountItem[] { + return ( + JSON.parse(fs.readFileSync(this._soloDataPath).toString()) as any[] + ).filter((e) => e.loginSessionId === loginSessionId); } - private async _saveSoloAccountItems(items: AccountItem[]) { - await fs.promises.writeFile(this._soloDataPath, JSON.stringify(items)); + private _saveSoloAccountItems(items: AccountItem[]) { + fs.writeFileSync(this._soloDataPath, JSON.stringify(items)); } async getAccountItems(loginSessionId: string): Promise { let accountItems: AccountItem[], soloUpdate = false; if (this.isInSoloMode) { - accountItems = await this._getSoloAccountItems(loginSessionId); + accountItems = this._getSoloAccountItems(loginSessionId); } else { accountItems = await this.mongodbCollection .find({ loginSessionId }) @@ -79,7 +76,7 @@ export class AccountInventoryManager { } if (this.isInSoloMode && soloUpdate) { - await this._saveSoloAccountItems(accountItems); + this._saveSoloAccountItems(accountItems); } return accountItems; @@ -90,7 +87,7 @@ export class AccountInventoryManager { itemDefinitionId: number ): Promise { if (this.isInSoloMode) { - const accountItems = await this._getSoloAccountItems(loginSessionId); + const accountItems = this._getSoloAccountItems(loginSessionId); return ( accountItems.find((v) => { return v.itemDefinitionId === itemDefinitionId; @@ -108,7 +105,7 @@ export class AccountInventoryManager { let accountItems: AccountItem[]; if (this.isInSoloMode) { - accountItems = await this._getSoloAccountItems(loginSessionId); + accountItems = this._getSoloAccountItems(loginSessionId); } else { accountItems = await this.getAccountItems(loginSessionId); } @@ -127,7 +124,7 @@ export class AccountInventoryManager { } else { accountItems.push({ loginSessionId, ...item } as any); } - await this._saveSoloAccountItems(accountItems); + this._saveSoloAccountItems(accountItems); } else { if (storedItem) { storedItem.stackCount++; @@ -152,7 +149,7 @@ export class AccountInventoryManager { } async updateAccountItem(loginSessionId: string, item: BaseItem) { if (this.isInSoloMode) { - const accountItems = await this._getSoloAccountItems(loginSessionId); + const accountItems = this._getSoloAccountItems(loginSessionId); for (let i = 0; i < accountItems.length; i++) { const originalItem = accountItems[i]; if (originalItem.itemDefinitionId === item.itemDefinitionId) { @@ -160,7 +157,7 @@ export class AccountInventoryManager { break; } } - await this._saveSoloAccountItems(accountItems); + this._saveSoloAccountItems(accountItems); } else { return await this.mongodbCollection.updateOne( { @@ -178,7 +175,7 @@ export class AccountInventoryManager { } async removeAccountItem(loginSessionId: string, item: BaseItem) { if (this.isInSoloMode) { - const accountItems = await this._getSoloAccountItems(loginSessionId); + const accountItems = this._getSoloAccountItems(loginSessionId); for (let i = 0; i < accountItems.length; i++) { const originalItem = accountItems[i]; if (originalItem.itemDefinitionId === item.itemDefinitionId) { @@ -186,7 +183,7 @@ export class AccountInventoryManager { break; } } - await this._saveSoloAccountItems(accountItems); + this._saveSoloAccountItems(accountItems); } else { return await this.mongodbCollection.deleteOne({ loginSessionId, diff --git a/src/servers/ZoneServer2016/managers/aimanager.ts b/src/servers/ZoneServer2016/managers/aimanager.ts index 2281d47768..6f617f7e12 100644 --- a/src/servers/ZoneServer2016/managers/aimanager.ts +++ b/src/servers/ZoneServer2016/managers/aimanager.ts @@ -19,11 +19,6 @@ import { ZoneServer2016 } from "../zoneserver"; const degradeTrapsCallTime = 1300_000; const ttlExplosives = 3600_000 * 3; -/** Largest triggerRadiusX across all TrapEntity item types (spike trap = 5m) */ -const MAX_TRAP_TRIGGER_RADIUS = 5; -/** Walk-into radius for ExplosiveEntity trigger */ -const EXPLOSIVE_TRIGGER_RADIUS = 0.6; -const EXPLOSIVE_TRIGGER_RADIUS_Y = 0.5; export class AiManager { trapEntities: Set = new Set(); playerEntities: Set = new Set(); @@ -75,57 +70,36 @@ export class AiManager { } private checkTraps() { - if (this.trapEntities.size === 0 || this.playerEntities.size === 0) return; - // Instead of checking every trigger against every player, we only check players at nearby cells. The grid range is large enough to cover the biggest trigger radius we use (5m for spike traps). this.playerEntities.forEach((player) => { - if (!player.isAlive) return; - const cells = this.server.getGridCellsInRadius( - player.state.position, - MAX_TRAP_TRIGGER_RADIUS - ); - for (const cell of cells) { - for (const obj of cell.objects) { - if (!(obj instanceof TrapEntity)) continue; - if (obj.lastTrigger + obj.cooldown > this.now) continue; - if ( - isPosInRadiusWithY( - obj.triggerRadiusX, - player.state.position, - obj.state.position, - obj.triggerRadiusY - ) - ) { - obj.detonate(player.characterId); - } + this.trapEntities.forEach((trap) => { + if (trap.lastTrigger + trap.cooldown > this.now) { + return; } - } + const inRadius = isPosInRadiusWithY( + trap.triggerRadiusX, + player.state.position, + trap.state.position, + trap.triggerRadiusY + ); + if (player.isAlive && inRadius) { + trap.detonate(player.characterId); + } + }); }); } private checkExplosive() { - if (this.explosiveEntities.size === 0 || this.playerEntities.size === 0) - return; this.playerEntities.forEach((player) => { - if (!player.isAlive) return; - const cells = this.server.getGridCellsInRadius( - player.state.position, - EXPLOSIVE_TRIGGER_RADIUS - ); - for (const cell of cells) { - for (const obj of cell.objects) { - if (!(obj instanceof ExplosiveEntity)) continue; - if (!obj.isArmed) continue; - if ( - isPosInRadiusWithY( - EXPLOSIVE_TRIGGER_RADIUS, - player.state.position, - obj.state.position, - EXPLOSIVE_TRIGGER_RADIUS_Y - ) - ) { - obj.detonate(player.characterId); - } + this.explosiveEntities.forEach((explosive) => { + const inRadius = isPosInRadiusWithY( + 0.6, + player.state.position, + explosive.state.position, + 0.5 + ); + if (player.isAlive && inRadius) { + explosive.detonate(player.characterId); } - } + }); }); } private degradeTraps() { diff --git a/src/servers/ZoneServer2016/managers/chatmanager.ts b/src/servers/ZoneServer2016/managers/chatmanager.ts index d04fb9906b..776f7d9fa0 100644 --- a/src/servers/ZoneServer2016/managers/chatmanager.ts +++ b/src/servers/ZoneServer2016/managers/chatmanager.ts @@ -17,10 +17,7 @@ import { ZoneClient2016 as Client } from "../classes/zoneclient"; import { ZoneServer2016 } from "../zoneserver"; import { getDateString } from "../../../utils/utils"; import { ChatChatText } from "types/zone2016packets"; -import { PluginManager } from "./pluginmanager"; -const blacklist = PluginManager.loadServerData( - "2016/sampleData/blacklisted_words.json" -); +const blacklist = require("../../../..//data/2016/sampleData/blacklisted_words.json"); export class ChatManager { sendChatText( diff --git a/src/servers/ZoneServer2016/managers/configmanager.ts b/src/servers/ZoneServer2016/managers/configmanager.ts index 0916d798a5..9615b98580 100644 --- a/src/servers/ZoneServer2016/managers/configmanager.ts +++ b/src/servers/ZoneServer2016/managers/configmanager.ts @@ -31,13 +31,11 @@ function fileExists(filePath: string): boolean { export class ConfigManager { private defaultConfig: Config; private config: Config; - private configPath: string; constructor( server: ZoneServer2016, configPath: string = `${process.cwd()}/config.yaml` ) { - this.configPath = configPath; this.defaultConfig = this.loadYaml( "/../../../../data/2016/sampleData/defaultconfig.yaml" ) as Config; @@ -55,7 +53,6 @@ export class ConfigManager { ); this.config = this.defaultConfig; - this.applyEnvOverrides(); this.applyConfig(server); console.log("Default config loaded!"); return; @@ -64,7 +61,6 @@ export class ConfigManager { const config = this.loadYaml(configPath, false); if (config) { this.config = this.loadConfig(config); - this.applyEnvOverrides(); this.applyConfig(server); console.log("Config Loaded!"); return; @@ -72,29 +68,9 @@ export class ConfigManager { console.error("Config failed to load! Using default."); this.config = this.defaultConfig; - this.applyEnvOverrides(); this.applyConfig(server); } - reload(server: ZoneServer2016): boolean { - if (!this.configPath || !fileExists(this.configPath)) { - console.error("Config path is invalid! Cannot reload."); - return false; - } - - const config = this.loadYaml(this.configPath, false); - if (!config) { - console.error("Config failed to reload!"); - return false; - } - - this.config = this.loadConfig(config); - this.applyEnvOverrides(); - this.applyConfig(server); - console.log("Config reloaded!"); - return true; - } - public loadYaml(path: string, relative = true): Config | undefined { return yaml.load( fs.readFileSync(`${relative ? __dirname : ""}${path}`, "utf8") @@ -182,164 +158,6 @@ export class ConfigManager { }; } - /** - * Applies environment variable overrides to this.config. - * - * Naming convention:
_ - * where FIELD_NAME is the camelCase field converted to SCREAMING_SNAKE_CASE. - * - * Examples: - * SERVER_GAME_MODE, SERVER_IS_PVE, RCON_PORT, RCON_PASSWORD, - * WORLDOBJECTS_GRID_SCRAP_LIMIT, VOICECHAT_SERVER_ADDRESS, ... - * - * Backward-compat aliases (old names still work): - * GRID_SCRAP_LIMIT -> WORLDOBJECTS_GRID_SCRAP_LIMIT - * GRID_SCRAP_LIMIT_ENABLED -> WORLDOBJECTS_GRID_SCRAP_LIMIT_ENABLED - * VOICE_CHAT_SERVER_ADDRESS -> VOICECHAT_SERVER_ADDRESS - * - * Array/object fields (e.g. fairplay.acceptedRejectionTypes, - * decay.dailyRepairMaterials) are not overridable via env vars. - */ - private applyEnvOverrides(): void { - const normalizedEnv = new Map( - Object.entries(process.env) - .filter((entry): entry is [string, string] => entry[1] !== undefined) - .map(([k, v]) => [k.toLowerCase(), v]) - ); - - this.applyEnvToSection( - "SERVER", - this.config.server as unknown as Record, - normalizedEnv - ); - this.applyEnvToSection( - "RCON", - this.config.rcon as unknown as Record, - normalizedEnv - ); - this.applyEnvToSection( - "CHALLENGES", - this.config.challenges as unknown as Record, - normalizedEnv - ); - this.applyEnvToSection( - "VOICECHAT", - this.config.voicechat as unknown as Record, - normalizedEnv - ); - this.applyEnvToSection( - "FAIRPLAY", - this.config.fairplay as unknown as Record, - normalizedEnv - ); - this.applyEnvToSection( - "WEATHER", - this.config.weather as unknown as Record, - normalizedEnv - ); - this.applyEnvToSection( - "AIRDROP", - this.config.airdrop as unknown as Record, - normalizedEnv - ); - this.applyEnvToSection( - "GAMETIME", - this.config.gametime as unknown as Record, - normalizedEnv - ); - this.applyEnvToSection( - "WORLDOBJECTS", - this.config.worldobjects as unknown as Record, - normalizedEnv - ); - this.applyEnvToSection( - "SPEEDTREE", - this.config.speedtree as unknown as Record, - normalizedEnv - ); - this.applyEnvToSection( - "CONSTRUCTION", - this.config.construction as unknown as Record, - normalizedEnv - ); - this.applyEnvToSection( - "DECAY", - this.config.decay as unknown as Record, - normalizedEnv - ); - this.applyEnvToSection( - "SMELTING", - this.config.smelting as unknown as Record, - normalizedEnv - ); - this.applyEnvToSection( - "RANDOMEVENTS", - this.config.randomevents as unknown as Record, - normalizedEnv - ); - this.applyEnvToSection( - "GROUPS", - this.config.groups as unknown as Record, - normalizedEnv - ); - - // Backward-compat aliases - const gridScrapLimit = normalizedEnv.get("grid_scrap_limit"); - if (gridScrapLimit !== undefined) { - const n = parseInt(gridScrapLimit, 10); - if (!isNaN(n)) this.config.worldobjects.gridScrapLimit = n; - } - const gridScrapLimitEnabled = normalizedEnv.get("grid_scrap_limit_enabled"); - if (gridScrapLimitEnabled !== undefined) { - this.config.worldobjects.gridScrapLimitEnabled = - gridScrapLimitEnabled.toLowerCase() === "true"; - } - const voiceChatServerAddress = normalizedEnv.get( - "voice_chat_server_address" - ); - if (voiceChatServerAddress !== undefined) { - this.config.voicechat.serverAddress = voiceChatServerAddress; - } - } - - // Overrides for fields where the automatic camelCase→SCREAMING_SNAKE - // conversion produces an unintuitive name (e.g. isPvE → IS_PVE, not IS_PV_E). - private static readonly fieldKeyOverrides: Record = { - isPvE: "IS_PVE" - }; - - /** - * Iterates all primitive (non-array, non-object) fields in a config section - * and applies matching environment variables using the naming convention - * `${prefix}_${SCREAMING_SNAKE_CASE_FIELD}`. - * Lookup is case-insensitive. - */ - private applyEnvToSection( - prefix: string, - section: Record, - normalizedEnv: Map - ): void { - for (const [field, currentValue] of Object.entries(section)) { - if (currentValue === null || typeof currentValue === "object") continue; - - const fieldKey = - ConfigManager.fieldKeyOverrides[field] ?? - field.replace(/([a-z])([A-Z])/g, "$1_$2").toUpperCase(); - const envKey = `${prefix}_${fieldKey}`.toLowerCase(); - const raw = normalizedEnv.get(envKey); - if (raw === undefined) continue; - - if (typeof currentValue === "number") { - const n = parseFloat(raw); - if (!isNaN(n)) section[field] = n; - } else if (typeof currentValue === "boolean") { - section[field] = raw.toLowerCase() === "true"; - } else { - section[field] = raw; - } - } - } - applyConfig(server: ZoneServer2016) { //#region server const { @@ -347,6 +165,7 @@ export class ConfigManager { proximityItemsDistance, interactionDistance, charactersRenderDistance, + tickRate, worldRoutineRate, welcomeMessage, adminMessage, @@ -360,8 +179,7 @@ export class ConfigManager { baseConstructionDamage, damageWeapons, disablePOIManager, - disableMapBoundsCheck, - disableBaseCheck + disableMapBoundsCheck } = this.config.server; server.gameMode = GameModes[gameMode.trim().toUpperCase() as keyof typeof GameModes] ?? @@ -369,6 +187,7 @@ export class ConfigManager { server.proximityItemsDistance = proximityItemsDistance; server.interactionDistance = interactionDistance; server.charactersRenderDistance = charactersRenderDistance; + server.tickRate = tickRate; server.worldRoutineRate = worldRoutineRate; server.welcomeMessage = welcomeMessage; server.adminMessage = adminMessage; @@ -383,7 +202,6 @@ export class ConfigManager { server.damageWeapons = damageWeapons; server.disablePOIManager = disablePOIManager; server.disableMapBoundsCheck = disableMapBoundsCheck; - server.disableBaseCheck = disableBaseCheck; //#endregion //#region Rcon @@ -412,7 +230,8 @@ export class ConfigManager { server.voiceChatManager.useVoiceChatV2 = useVoiceChatV2; server.voiceChatManager.joinVoiceChatOnConnect = joinVoiceChatOnConnect; server.voiceChatManager.serverAccessToken = serverAccessToken; - server.voiceChatManager.serverAddress = serverAddress; + server.voiceChatManager.serverAddress = + process.env.VOICE_CHAT_SERVER_ADDRESS || serverAddress; //#endregion //#region fairplay @@ -461,9 +280,6 @@ export class ConfigManager { itemDespawnTimer, lootDespawnTimer, deadNpcDespawnTimer, - maxNpcDespawnsPerRun, - maxLootbagDespawnsPerRun, - maxItemDespawnsPerRun, chanceWornLetter, vehicleSpawnRadius, npcSpawnRadius, @@ -471,9 +287,7 @@ export class ConfigManager { chanceScreamer, lootbagDespawnTimer, crowbarHitRewardChance, - crowbarHitDamage, - gridScrapLimit, - gridScrapLimitEnabled + crowbarHitDamage } = this.config.worldobjects; server.worldObjectManager.vehicleSpawnCap = vehicleSpawnCap; server.worldObjectManager.hasCustomLootRespawnTime = @@ -486,10 +300,6 @@ export class ConfigManager { server.worldObjectManager.lootDespawnTimer = lootDespawnTimer; server.worldObjectManager.deadNpcDespawnTimer = deadNpcDespawnTimer; server.worldObjectManager.lootbagDespawnTimer = lootbagDespawnTimer; - server.worldObjectManager.maxNpcDespawnsPerRun = maxNpcDespawnsPerRun; - server.worldObjectManager.maxLootbagDespawnsPerRun = - maxLootbagDespawnsPerRun; - server.worldObjectManager.maxItemDespawnsPerRun = maxItemDespawnsPerRun; server.worldObjectManager.vehicleSpawnRadius = vehicleSpawnRadius; server.worldObjectManager.npcSpawnRadius = npcSpawnRadius; @@ -504,9 +314,6 @@ export class ConfigManager { server.crowbarHitRewardChance = crowbarHitRewardChance; server.crowbarHitDamage = crowbarHitDamage; - - server.worldObjectManager.gridScrapLimit = gridScrapLimit; - server.worldObjectManager.gridScrapLimitEnabled = gridScrapLimitEnabled; //#endregion //#region speedtree diff --git a/src/servers/ZoneServer2016/managers/constructiondecayworker.ts b/src/servers/ZoneServer2016/managers/constructiondecayworker.ts deleted file mode 100644 index 76dcddbf26..0000000000 --- a/src/servers/ZoneServer2016/managers/constructiondecayworker.ts +++ /dev/null @@ -1,226 +0,0 @@ -// ====================================================================== -// -// GNU GENERAL PUBLIC LICENSE -// Version 3, 29 June 2007 -// copyright (C) 2020 - 2021 Quentin Gruber -// copyright (C) 2021 - 2026 H1emu community -// -// https://github.com/QuentinGruber/h1z1-server -// https://www.npmjs.com/package/h1z1-server -// -// Based on https://github.com/psemu/soe-network -// ====================================================================== - -import { Worker, isMainThread, parentPort } from "node:worker_threads"; - -export interface EntityDecaySnapshot { - characterId: string; - isDecayProtected: boolean; - maxHealth: number; - /** Pre-computed on main thread: true = skip decay (ramps, stairs, foundations, expansions) */ - skipDecay: boolean; -} - -export interface ConstructionDecayRequest { - requestId: number; - type: "decayDamage"; - payload: { - ticksToFullDecay: number; - worldFreeplaceDecayMultiplier: number; - foundations: EntityDecaySnapshot[]; - worldLootable: EntityDecaySnapshot[]; - worldSimple: EntityDecaySnapshot[]; - constructionSimple: EntityDecaySnapshot[]; - lootableConstruction: EntityDecaySnapshot[]; - constructionDoors: EntityDecaySnapshot[]; - }; -} - -export interface DecayDamageResult { - entitiesToDamage: { characterId: string; damage: number }[]; - decayProtectedResets: string[]; -} - -interface ConstructionDecayResponse { - requestId: number; - type: "decayDamage"; - result: DecayDamageResult; - error?: string; -} - -// ── Worker thread logic ────────────────────────────────────────────── - -function processEntities( - entities: EntityDecaySnapshot[], - ticksToFullDecay: number, - multiplier: number, - entitiesToDamage: { characterId: string; damage: number }[], - decayProtectedResets: string[] -) { - for (const entity of entities) { - if (entity.skipDecay) continue; - if (entity.isDecayProtected) { - decayProtectedResets.push(entity.characterId); - continue; - } - entitiesToDamage.push({ - characterId: entity.characterId, - damage: entity.maxHealth / (ticksToFullDecay / multiplier) - }); - } -} - -function computeDecayDamage( - payload: ConstructionDecayRequest["payload"] -): DecayDamageResult { - const entitiesToDamage: { characterId: string; damage: number }[] = []; - const decayProtectedResets: string[] = []; - const { ticksToFullDecay, worldFreeplaceDecayMultiplier } = payload; - - processEntities( - payload.foundations, - ticksToFullDecay, - 1, - entitiesToDamage, - decayProtectedResets - ); - processEntities( - payload.worldLootable, - ticksToFullDecay, - worldFreeplaceDecayMultiplier, - entitiesToDamage, - decayProtectedResets - ); - processEntities( - payload.worldSimple, - ticksToFullDecay, - worldFreeplaceDecayMultiplier, - entitiesToDamage, - decayProtectedResets - ); - processEntities( - payload.constructionSimple, - ticksToFullDecay, - 1, - entitiesToDamage, - decayProtectedResets - ); - processEntities( - payload.lootableConstruction, - ticksToFullDecay, - 1, - entitiesToDamage, - decayProtectedResets - ); - processEntities( - payload.constructionDoors, - ticksToFullDecay, - 1, - entitiesToDamage, - decayProtectedResets - ); - - return { entitiesToDamage, decayProtectedResets }; -} - -if (!isMainThread) { - parentPort!.on("message", (request: ConstructionDecayRequest) => { - try { - const result = computeDecayDamage(request.payload); - const response: ConstructionDecayResponse = { - requestId: request.requestId, - type: "decayDamage", - result - }; - parentPort!.postMessage(response); - } catch (e: any) { - const response: ConstructionDecayResponse = { - requestId: request.requestId, - type: "decayDamage", - result: { entitiesToDamage: [], decayProtectedResets: [] }, - error: e?.message ?? String(e) - }; - parentPort!.postMessage(response); - } - }); -} - -// ── Main thread host ───────────────────────────────────────────────── - -interface PendingRequest { - resolve: (value: DecayDamageResult) => void; - reject: (reason?: unknown) => void; -} - -export class ConstructionDecayWorker { - private readonly worker: Worker; - private requestId = 0; - private readonly pendingRequests: Map = new Map(); - private isStopped = false; - - constructor() { - this.worker = new Worker(__filename); - - this.worker.on("message", (message: ConstructionDecayResponse) => { - const pendingRequest = this.pendingRequests.get(message.requestId); - if (!pendingRequest) return; - this.pendingRequests.delete(message.requestId); - if (message.error) { - pendingRequest.reject(new Error(message.error)); - return; - } - pendingRequest.resolve(message.result); - }); - - this.worker.on("error", (error) => { - this.rejectAllPending(error); - }); - - this.worker.on("exit", (code) => { - this.isStopped = true; - if (code !== 0) { - this.rejectAllPending( - new Error(`Construction decay worker exited with code ${code}`) - ); - } - }); - } - - computeDecayDamage( - payload: ConstructionDecayRequest["payload"] - ): Promise { - const request: ConstructionDecayRequest = { - requestId: this.requestId++, - type: "decayDamage", - payload - }; - return this.request(request); - } - - async stop(): Promise { - if (this.isStopped) return; - this.isStopped = true; - this.rejectAllPending(new Error("Construction decay worker stopped")); - await this.worker.terminate(); - } - - private request( - request: ConstructionDecayRequest - ): Promise { - if (this.isStopped) { - return Promise.resolve({ - entitiesToDamage: [], - decayProtectedResets: [] - }); - } - return new Promise((resolve, reject) => { - this.pendingRequests.set(request.requestId, { resolve, reject }); - this.worker.postMessage(request); - }); - } - - private rejectAllPending(error: unknown): void { - this.pendingRequests.forEach((p) => p.reject(error)); - this.pendingRequests.clear(); - } -} diff --git a/src/servers/ZoneServer2016/managers/constructionmanager.ts b/src/servers/ZoneServer2016/managers/constructionmanager.ts index 101f05f968..683eb78fe7 100644 --- a/src/servers/ZoneServer2016/managers/constructionmanager.ts +++ b/src/servers/ZoneServer2016/managers/constructionmanager.ts @@ -11,6 +11,9 @@ // Based on https://github.com/psemu/soe-network // ====================================================================== +const Z1_vehicles = require("../../../../data/2016/zoneData/Z1_vehicleLocations.json"), + spawnLocations2 = require("../../../../data/2016/zoneData/Z1_gridSpawns.json"); + import { ConstructionEntity, dailyRepairMaterial, @@ -22,6 +25,7 @@ import { fixEulerOrder, getConstructionSlotId, getDistance, + isPosInPoi, isPosInRadius, isPosInRadiusWithY, movePoint @@ -60,14 +64,6 @@ import { ConstructionUnknown, PlayerUpdatePosition } from "types/zone2016packets"; -import { PluginManager } from "./pluginmanager"; - -const Z1_vehicles = PluginManager.loadServerData( - "2016/zoneData/Z1_vehicleLocations.json" - ), - spawnLocations2 = PluginManager.loadServerData( - "2016/zoneData/Z1_gridSpawns.json" - ); export class ConstructionManager { overridePlacementItems: Array = [ @@ -379,7 +375,6 @@ export class ConstructionManager { return false; } detectPOIPlacement( - server: ZoneServer2016, itemDefinitionId: number, position: Float32Array, client: Client, @@ -388,7 +383,7 @@ export class ConstructionManager { if (client.isDebugMode) return false; if (this.overridePlacementItems.includes(itemDefinitionId)) return false; - const isInPoi = server.isPosInPoi(position); + const isInPoi = isPosInPoi(position); // allow placement in poi if object is parented to a foundation if (isInPoi && !isInsidePermissionedFoundation) { return true; @@ -447,7 +442,6 @@ export class ConstructionManager { if ( server.isNoBuildInPois && this.detectPOIPlacement( - server, itemDefinitionId, position, client, @@ -1882,16 +1876,12 @@ export class ConstructionManager { true ); return true; - } else if ( - !server.disableBaseCheck && - (!client.isAdmin || !client.isDebugMode) - ) { + } else if (!client.isAdmin || !client.isDebugMode) { this.tpPlayerOutsideFoundation(server, client, foundation); } } } if (allowed) return false; - if (server.disableBaseCheck) return false; const bufferZone = 0.15; const position = client.character.state.position; const positions: Float32Array[] = []; @@ -1991,10 +1981,7 @@ export class ConstructionManager { true ); return true; - } else if ( - !server.disableBaseCheck && - (!client.isAdmin || !client.isDebugMode) - ) { + } else if (!client.isAdmin || !client.isDebugMode) { const damageInfo: DamageInfo = { entity: "Server.Permissions", damage: 99999 @@ -2092,7 +2079,7 @@ export class ConstructionManager { const hasPermission = isSameGroup && hasVisitPermission; if ( - iteratedClient.spawnedEntities.has(freePlacedEntity) && + iteratedClient.spawnedEntities.has(client.character) && iteratedClient.character.isHidden != freePlacedEntity.isHidden && !hasPermission ) { @@ -2100,10 +2087,10 @@ export class ConstructionManager { iteratedClient, "Character.RemovePlayer", { - characterId: freePlacedEntity.characterId + characterId: client.character.characterId } ); - iteratedClient.spawnedEntities.delete(freePlacedEntity); + iteratedClient.spawnedEntities.delete(client.character); } } } else return; @@ -2175,7 +2162,6 @@ export class ConstructionManager { }, 500); setTimeout(() => { if ( - !server.disableBaseCheck && foundation.isSecured && foundation.isInside(client.character.state.position) ) { @@ -2345,7 +2331,7 @@ export class ConstructionManager { client: Client, entity: ConstructionDoor ) { - if (client.spawnedEntities.has(entity)) return; + if (client.spawnedEntities.has(entity) || !client.isSynced) return; server.addLightweightNpc( client, entity, diff --git a/src/servers/ZoneServer2016/managers/decaymanager.test.ts b/src/servers/ZoneServer2016/managers/decaymanager.test.ts index 1a291f69e8..9d025b1bfd 100644 --- a/src/servers/ZoneServer2016/managers/decaymanager.test.ts +++ b/src/servers/ZoneServer2016/managers/decaymanager.test.ts @@ -48,7 +48,7 @@ test("decaymanager", { timeout: 10000 }, async (t) => { zone.decayManager.run(zone); assert.strictEqual(Object.keys(zone._constructionFoundations).length, 0); }); - await t.test("test decay", async () => { + await t.test("test decay", () => { zone._constructionFoundations = {}; const characterId = generate_random_guid(); const transientId = zone.getTransientId(characterId); @@ -69,11 +69,9 @@ test("decaymanager", { timeout: 10000 }, async (t) => { zone._constructionFoundations[characterId] = foundation; zone.decayManager.griefCheckSlotAmount = 0; - zone.decayManager.useDecayWorker = false; t.mock.timers.tick(originalDate); for (let i = 0; i < 10_000; i++) { - zone.decayManager.clearTimers(); - await zone.decayManager.run(zone); + zone.decayManager.run(zone); } assert.strictEqual(Object.keys(zone._constructionFoundations).length, 0); }); diff --git a/src/servers/ZoneServer2016/managers/decaymanager.ts b/src/servers/ZoneServer2016/managers/decaymanager.ts index 0a73f3b03b..32d0ddd3bd 100644 --- a/src/servers/ZoneServer2016/managers/decaymanager.ts +++ b/src/servers/ZoneServer2016/managers/decaymanager.ts @@ -10,27 +10,18 @@ // Based on https://github.com/psemu/soe-network // ====================================================================== -import { scheduler } from "timers/promises"; import { ZoneServer2016 } from "../zoneserver"; import { Items, ResourceIds, ResourceTypes } from "../models/enums"; import { LootableConstructionEntity } from "../entities/lootableconstructionentity"; import { ConstructionDoor } from "../entities/constructiondoor"; import { ConstructionChildEntity } from "../entities/constructionchildentity"; import { getDistance } from "../../../utils/utils"; -import { Vehicle2016 as Vehicle } from "../entities/vehicle"; import { ConstructionParentEntity } from "../entities/constructionparententity"; +import { Vehicle2016 } from "../entities/vehicle"; import { dailyRepairMaterial } from "types/zoneserver"; import { BaseItem } from "../classes/baseItem"; -import { - ConstructionDecayWorker, - EntityDecaySnapshot -} from "./constructiondecayworker"; export class DecayManager { - /** MANAGED BY CONFIGMANAGER — offloads decay damage computation to a worker thread */ - useDecayWorker: boolean = true; - private _decayWorker?: ConstructionDecayWorker; - /** Used for tracking the tick amount needed before decay damage occurs on the construction */ constructionDamageTickCount = 0; @@ -56,18 +47,10 @@ export class DecayManager { public clearTimers() { if (this.runTimer) clearTimeout(this.runTimer); - this._decayWorker?.stop(); } - private getOrCreateWorker(): ConstructionDecayWorker { - if (!this._decayWorker) { - this._decayWorker = new ConstructionDecayWorker(); - } - return this._decayWorker; - } - - public async run(server: ZoneServer2016) { - await this.contructionExpirationCheck(server); + public run(server: ZoneServer2016) { + this.contructionExpirationCheck(server); if (this.constructionDamageTickCount >= this.constructionDamageTicks) { this.contructionDecayDamage(server); this.constructionDamageTickCount = -1; @@ -81,17 +64,14 @@ export class DecayManager { this.vehicleDamageTickCount++; this.runTimer = setTimeout(() => { - void this.run(server); + this.run(server); }, this.decayTickInterval); } - private async contructionExpirationCheck(server: ZoneServer2016) { + private contructionExpirationCheck(server: ZoneServer2016) { let destroyedGriefFoundations = 0; - let i = 0; for (const a in server._constructionFoundations) { - if (++i % 100 === 0) await scheduler.yield(); const foundation = server._constructionFoundations[a]; - if (!foundation) continue; if ( foundation.itemDefinitionId == Items.FOUNDATION || foundation.itemDefinitionId == Items.GROUND_TAMPER @@ -255,22 +235,11 @@ export class DecayManager { } contructionDecayDamage(server: ZoneServer2016) { - // Run repair boxes first (needs live server refs, stays on main thread) for (const a in server._constructionFoundations) { - this.useRepairBox(server, server._constructionFoundations[a]); - } + const foundation = server._constructionFoundations[a]; - if (this.useDecayWorker) { - this._contructionDecayDamageWorker(server); - } else { - this._contructionDecayDamageSync(server); - } - } + this.useRepairBox(server, foundation); - /** Synchronous fallback — original logic */ - private _contructionDecayDamageSync(server: ZoneServer2016) { - for (const a in server._constructionFoundations) { - const foundation = server._constructionFoundations[a]; if ( foundation.itemDefinitionId != Items.FOUNDATION && foundation.itemDefinitionId != Items.GROUND_TAMPER && @@ -279,6 +248,7 @@ export class DecayManager { this.decayDamage(server, foundation); } } + for (const a in server._worldLootableConstruction) { this.decayDamage( server, @@ -301,137 +271,45 @@ export class DecayManager { ) { continue; } - this.decayDamage(server, simple); + this.decayDamage(server, server._constructionSimple[a]); } for (const a in server._lootableConstruction) { this.decayDamage(server, server._lootableConstruction[a]); } for (const a in server._constructionDoors) { - this.decayDamage(server, server._constructionDoors[a]); + const door = server._constructionDoors[a]; + this.decayDamage(server, door); } } - /** Worker-based path — serializes snapshots, applies action list on main thread */ - private _contructionDecayDamageWorker(server: ZoneServer2016) { - const toSnapshot = ( - entity: - | LootableConstructionEntity - | ConstructionDoor - | ConstructionChildEntity - | ConstructionParentEntity, - skipDecay: boolean - ): EntityDecaySnapshot => ({ - characterId: entity.characterId, - isDecayProtected: entity.isDecayProtected, - maxHealth: entity.maxHealth, - skipDecay - }); - - const foundations: EntityDecaySnapshot[] = []; - for (const a in server._constructionFoundations) { - const f = server._constructionFoundations[a]; - const skip = - f.itemDefinitionId === Items.FOUNDATION || - f.itemDefinitionId === Items.GROUND_TAMPER || - f.itemDefinitionId === Items.FOUNDATION_EXPANSION; - foundations.push(toSnapshot(f, skip)); - } - - const worldLootable = Object.values(server._worldLootableConstruction).map( - (e) => toSnapshot(e, false) - ); - const worldSimple = Object.values(server._worldSimpleConstruction).map( - (e) => toSnapshot(e, false) - ); - const constructionSimple: EntityDecaySnapshot[] = []; - for (const a in server._constructionSimple) { - const s = server._constructionSimple[a]; - const skip = - s.itemDefinitionId === Items.FOUNDATION_RAMP || - s.itemDefinitionId === Items.FOUNDATION_STAIRS; - constructionSimple.push(toSnapshot(s, skip)); + private getCloseVehicles(server: ZoneServer2016, vehicle: Vehicle2016) { + const vehicles: Array = []; + for (const characterId in server._vehicles) { + const v = server._vehicles[characterId]; + if (!vehicle) continue; + if ( + getDistance(vehicle.state.position, v.state.position) <= + this.vehicleDamageRange + ) { + vehicles.push(v.characterId); + } } - const lootableConstruction = Object.values( - server._lootableConstruction - ).map((e) => toSnapshot(e, false)); - const constructionDoors = Object.values(server._constructionDoors).map( - (e) => toSnapshot(e, false) - ); - - this.getOrCreateWorker() - .computeDecayDamage({ - ticksToFullDecay: this.ticksToFullDecay, - worldFreeplaceDecayMultiplier: this.worldFreeplaceDecayMultiplier, - foundations, - worldLootable, - worldSimple, - constructionSimple, - lootableConstruction, - constructionDoors - }) - .then(({ entitiesToDamage, decayProtectedResets }) => { - // Reset isDecayProtected flags - for (const charId of decayProtectedResets) { - const entity = server.getConstructionEntity(charId); - if (entity) entity.isDecayProtected = false; - } - // Apply damage - for (const { characterId, damage } of entitiesToDamage) { - const entity = server.getConstructionEntity(characterId); - if (!entity) continue; - entity.damage(server, { entity: "Server.DecayManager", damage }); - } - }) - .catch((err) => { - console.error("Construction decay worker error:", err); - }); + return vehicles; } public vehicleDecayDamage(server: ZoneServer2016) { - const vehicleEntries = Object.values(server._vehicles); - const n = vehicleEntries.length; - if (n === 0) return; - - // Build a spatial hash to avoid comparing every entity against every other one. - // Each cell is sized to twice the damage range, so checking the surrounding - // 3×3 cells is enough to find all nearby candidates. - const cellSize = this.vehicleDamageRange * 2 || 100; - const spatialHash = new Map(); - for (const v of vehicleEntries) { - const pos = v.state.position; - const key = `${Math.floor(pos[0] / cellSize)},${Math.floor(pos[2] / cellSize)}`; - let bucket = spatialHash.get(key); - if (!bucket) { - bucket = []; - spatialHash.set(key, bucket); - } - bucket.push(v); - } - - for (const vehicle of vehicleEntries) { - const pos = vehicle.state.position; - const cx = Math.floor(pos[0] / cellSize); - const cz = Math.floor(pos[2] / cellSize); - let neighbors = 0; - for (let dx = -1; dx <= 1; dx++) { - for (let dz = -1; dz <= 1; dz++) { - const bucket = spatialHash.get(`${cx + dx},${cz + dz}`); - if (!bucket) continue; - for (const other of bucket) { - if (other === vehicle) continue; - if ( - getDistance(pos, other.state.position) <= this.vehicleDamageRange - ) - neighbors++; - } - } - } - + for (const characterId in server._vehicles) { + const vehicle = server._vehicles[characterId]; + if (!vehicle) continue; + const closeVehicles = this.getCloseVehicles(server, vehicle); let damage = this.baseVehicleDamage; - if (neighbors >= this.maxVehiclesPerArea) - damage *= neighbors - this.maxVehiclesPerArea + 1; - - vehicle.damage(server, { entity: "Server.DecayManager", damage }); + if (closeVehicles.length > this.maxVehiclesPerArea) { + damage *= closeVehicles.length - this.maxVehiclesPerArea + 1; + } + vehicle.damage(server, { + entity: "Server.DecayManager", + damage: damage + }); server.updateResourceToAllWithSpawnedEntity( vehicle.characterId, vehicle._resources[ResourceIds.CONDITION], diff --git a/src/servers/ZoneServer2016/managers/explosion.worker.ts b/src/servers/ZoneServer2016/managers/explosion.worker.ts deleted file mode 100644 index 8e4efcce6b..0000000000 --- a/src/servers/ZoneServer2016/managers/explosion.worker.ts +++ /dev/null @@ -1,230 +0,0 @@ -// ====================================================================== -// -// GNU GENERAL PUBLIC LICENSE -// Version 3, 29 June 2007 -// copyright (C) 2020 - 2021 Quentin Gruber -// copyright (C) 2021 - 2026 H1emu community -// -// https://github.com/QuentinGruber/h1z1-server -// https://www.npmjs.com/package/h1z1-server -// -// Based on https://github.com/psemu/soe-network -// ====================================================================== - -import { parentPort } from "worker_threads"; - -// ---- Constants (must stay in sync with explosionmanager.ts) ---------- -const EXPLOSION_CHARACTER_RADIUS = 5; -const EXPLOSION_CHARACTER_Y_RADIUS = 3; -const EXPLOSION_VEHICLE_RADIUS = 5; - -// ---- Snapshot types sent from the main thread ------------------------ - -export interface ExplosionEntry { - x: number; - y: number; - z: number; - /** charBaseDamage(entity) — pre-computed on main thread */ - charBaseDamage: number; - /** vehicleBaseDamage(entity) — pre-computed on main thread */ - vehicleBaseDamage: number; - /** base construction damage after item-type multiplier — pre-computed */ - constructionDamage: number; - attackerId: string; - weaponId?: number; -} - -export interface CharacterSnapshot { - characterId: string; - x: number; - y: number; - z: number; -} - -export interface VehicleSnapshot { - vehicleId: string; - x: number; - y: number; - z: number; -} - -export interface ConstructionSnapshot { - entityId: string; - /** Effective position — fixedPosition if present, else state.position */ - x: number; - y: number; - z: number; - damageRange: number; - /** Pre-computed hit radius — isSimple ? 4 : damageRange*1.5 (or damageRange for doors) */ - explosionRadius: number; -} - -export interface ChunkSnapshot { - explosions: ExplosionEntry[]; - characters: CharacterSnapshot[]; - vehicles: VehicleSnapshot[]; - construction: ConstructionSnapshot[]; -} - -// ---- Plan types returned to the main thread -------------------------- - -export interface CharacterDamageEntry { - characterId: string; - damage: number; - attackerId: string; - weapon?: number; -} - -export interface VehicleDamageEntry { - vehicleId: string; - damage: number; - attackerId: string; -} - -export interface ConstructionDamageEntry { - entityId: string; - totalDamage: number; - attackerId: string; -} - -export interface ExplosionPlan { - characterDamages: CharacterDamageEntry[]; - vehicleDamages: VehicleDamageEntry[]; - constructionDamages: ConstructionDamageEntry[]; -} - -// ---- Wire protocol --------------------------------------------------- - -interface WorkerRequest { - requestId: number; - snapshot: ChunkSnapshot; -} - -interface WorkerResponse { - requestId: number; - plan?: ExplosionPlan; - error?: string; -} - -// ---- Pure computation ------------------------------------------------ - -function sqDist( - ax: number, - ay: number, - az: number, - bx: number, - by: number, - bz: number -): number { - const dx = ax - bx, - dy = ay - by, - dz = az - bz; - return dx * dx + dy * dy + dz * dz; -} - -function processChunk(snapshot: ChunkSnapshot): ExplosionPlan { - const { explosions, characters, vehicles, construction } = snapshot; - - // ---- Characters ----------------------------------------------------- - const characterDamages: CharacterDamageEntry[] = []; - for (const char of characters) { - let totalDamage = 0; - let lastAttacker = ""; - let lastWeapon: number | undefined; - - for (const exp of explosions) { - const dx = char.x - exp.x; - if (Math.abs(dx) > EXPLOSION_CHARACTER_RADIUS) continue; - const dz = char.z - exp.z; - if (Math.abs(dz) > EXPLOSION_CHARACTER_RADIUS) continue; - const dy = char.y - exp.y; - if (Math.abs(dy) > EXPLOSION_CHARACTER_Y_RADIUS) continue; - - const d = Math.sqrt(dx * dx + dy * dy + dz * dz); - totalDamage += d > 1 ? exp.charBaseDamage / d : exp.charBaseDamage; - lastAttacker = exp.attackerId; - lastWeapon = exp.weaponId; - } - - if (totalDamage >= 1 && lastAttacker) { - characterDamages.push({ - characterId: char.characterId, - damage: Math.floor(totalDamage), - attackerId: lastAttacker, - weapon: lastWeapon - }); - } - } - - // ---- Vehicles ------------------------------------------------------- - const vehicleDamages: VehicleDamageEntry[] = []; - for (const veh of vehicles) { - let totalDamage = 0; - let lastAttacker = ""; - - for (const exp of explosions) { - const sq = sqDist(veh.x, veh.y, veh.z, exp.x, exp.y, exp.z); - if (sq > EXPLOSION_VEHICLE_RADIUS * EXPLOSION_VEHICLE_RADIUS) continue; - const d = Math.sqrt(sq); - totalDamage += d > 1 ? exp.vehicleBaseDamage / d : exp.vehicleBaseDamage; - lastAttacker = exp.attackerId; - } - - if (totalDamage >= 1 && lastAttacker) { - vehicleDamages.push({ - vehicleId: veh.vehicleId, - damage: Math.floor(totalDamage), - attackerId: lastAttacker - }); - } - } - - // ---- Construction --------------------------------------------------- - // Accumulate total damage per entity across all explosions in the chunk - // so the main thread can apply a single damage() call per piece. - const constructionDamages: ConstructionDamageEntry[] = []; - for (const cons of construction) { - let totalDamage = 0; - let lastAttacker = ""; - - const rSq = cons.explosionRadius * cons.explosionRadius; - for (const exp of explosions) { - const dx = cons.x - exp.x, - dy = cons.y - exp.y, - dz = cons.z - exp.z; - const sq = dx * dx + dy * dy + dz * dz; - if (sq > rSq) continue; - - const d = Math.sqrt(sq); - const dmg = - d < cons.damageRange - ? exp.constructionDamage - : exp.constructionDamage / Math.sqrt(d); - totalDamage += dmg; - lastAttacker = exp.attackerId; - } - - if (totalDamage >= 1 && lastAttacker) { - constructionDamages.push({ - entityId: cons.entityId, - totalDamage: Math.floor(totalDamage), - attackerId: lastAttacker - }); - } - } - - return { characterDamages, vehicleDamages, constructionDamages }; -} - -// ---- Message loop ---------------------------------------------------- - -parentPort!.on("message", (req: WorkerRequest) => { - try { - const plan = processChunk(req.snapshot); - const res: WorkerResponse = { requestId: req.requestId, plan }; - parentPort!.postMessage(res); - } catch (e) { - const res: WorkerResponse = { requestId: req.requestId, error: String(e) }; - parentPort!.postMessage(res); - } -}); diff --git a/src/servers/ZoneServer2016/managers/explosionmanager.ts b/src/servers/ZoneServer2016/managers/explosionmanager.ts deleted file mode 100644 index 4e9d090701..0000000000 --- a/src/servers/ZoneServer2016/managers/explosionmanager.ts +++ /dev/null @@ -1,512 +0,0 @@ -// ====================================================================== -// -// GNU GENERAL PUBLIC LICENSE -// Version 3, 29 June 2007 -// copyright (C) 2020 - 2021 Quentin Gruber -// copyright (C) 2021 - 2026 H1emu community -// -// https://github.com/QuentinGruber/h1z1-server -// https://www.npmjs.com/package/h1z1-server -// -// Based on https://github.com/psemu/soe-network -// ====================================================================== - -import { ZoneServer2016 } from "../zoneserver"; -import { BaseEntity } from "../entities/baseentity"; -import { ExplosiveEntity } from "../entities/explosiveentity"; -import { ProjectileEntity } from "../entities/projectileentity"; -import { ConstructionChildEntity } from "../entities/constructionchildentity"; -import { ConstructionParentEntity } from "../entities/constructionparententity"; -import { ConstructionDoor } from "../entities/constructiondoor"; -import { ZoneClient2016 } from "../classes/zoneclient"; -import { isChristmasSeason } from "../../../utils/utils"; -import { Effects, Items } from "../models/enums"; -import { ExplosionWorker } from "./explosionworker"; -import type { - ChunkSnapshot, - ExplosionPlan, - ConstructionSnapshot -} from "./explosion.worker"; - -/** Max grid search radius covering all construction damageRange values (max ~9.75m with 1.5x) */ -const EXPLOSION_GRID_RADIUS = 15; -const EXPLOSION_CHARACTER_RADIUS = 5; -const EXPLOSION_CHARACTER_Y_RADIUS = 3; -/** Number of explosions to process per event-loop tick. - * Kept moderate so the event loop stays responsive under extreme load. */ -const CHUNK_SIZE = 200; - -/** Item IDs whose construction damage is not divided (IED / landmine = full base damage) */ -const FULL_DAMAGE_IDS = new Set([Items.IED, Items.LANDMINE]); -/** Shack foundations that can receive direct explosion damage */ -const SHACK_IDS = new Set([Items.SHACK, Items.SHACK_SMALL, Items.SHACK_BASIC]); -/** Ramp/stairs construction that is immune to explosion damage */ -const IMMUNE_IDS = new Set([Items.FOUNDATION_RAMP, Items.FOUNDATION_STAIRS]); - -interface PendingExplosion { - entity: BaseEntity; - client?: ZoneClient2016; -} - -function charBaseDamage(entity: BaseEntity): number { - if (entity instanceof ExplosiveEntity) return 50000; - if (entity instanceof ProjectileEntity) { - return entity.actorModelId === 0 ? 8000 : 10000; - } - return 10000; -} - -function vehicleBaseDamage(entity: BaseEntity): number { - if (entity instanceof ExplosiveEntity) return 100000; - if (entity instanceof ProjectileEntity) { - return entity.actorModelId === 0 ? 50000 : 100000; - } - return 100000; -} - -function constructionBaseDamage( - entity: BaseEntity, - serverBase: number -): number { - if (entity instanceof ExplosiveEntity) { - if (FULL_DAMAGE_IDS.has(entity.itemDefinitionId)) return serverBase; - if (entity.itemDefinitionId === Items.FUEL_ETHANOL) return serverBase / 2.7; - if (entity.itemDefinitionId === Items.FUEL_BIOFUEL) return serverBase / 3; - return serverBase / 12; - } - return serverBase / 12; -} - -function attackerId(entity: BaseEntity): string { - if (entity instanceof ProjectileEntity) return entity.managerCharacterId; - return entity.characterId; -} - -function weaponId(entity: BaseEntity): number | undefined { - if (entity instanceof ExplosiveEntity) return entity.itemDefinitionId; - if (entity instanceof ProjectileEntity) return entity.itemDefinitionId; - return undefined; -} - -/** - * Batches all queued explosion events so that large simultaneous blasts - * are processed without blocking the main thread. - * - * Key optimisations vs. the previous implementation: - * 1. Worker thread — character, vehicle, and construction damage math runs - * off the main thread; the main thread only applies the resulting plan. - * 2. Construction deduplication — nearby construction entities are collected - * once per chunk (not once per explosion), so the grid lookup cost is - * O(unique_positions) rather than O(chunk_size). - * 3. Character AABB pre-filter — a bounding box covering all explosion - * positions in the chunk prunes distant characters before sending to - * the worker, keeping the postMessage payload small. - * 4. setImmediate chunking — yields the event loop between CHUNK_SIZE - * batches so network I/O can interleave. - * 5. Deduplication — each entity is queued at most once per flush cycle. - */ -export class ExplosionManager { - private readonly _server: ZoneServer2016; - private readonly _worker: ExplosionWorker; - /** Explosions waiting to be processed */ - private _pending: PendingExplosion[] = []; - /** characterIds already enqueued this cycle */ - private _queued = new Set(); - private _scheduled = false; - - constructor(server: ZoneServer2016) { - this._server = server; - this._worker = new ExplosionWorker(); - } - - /** - * Enqueue an explosion for batched processing. - * Returns false if the entity was already queued this cycle. - */ - queueExplosion(entity: BaseEntity, client?: ZoneClient2016): boolean { - if (this._queued.has(entity.characterId)) return false; - this._queued.add(entity.characterId); - this._pending.push({ entity, client }); - if (!this._scheduled) { - this._scheduled = true; - setImmediate(() => void this._flush()); - } - return true; - } - - async stop(): Promise { - await this._worker.stop(); - } - - // ---- Private -------------------------------------------------------- - - private async _flush(): Promise { - this._scheduled = false; - const batch = this._pending.splice(0); - this._queued.clear(); - if (batch.length === 0) return; - - for (let i = 0; i < batch.length; i += CHUNK_SIZE) { - const chunk = batch.slice(i, i + CHUNK_SIZE); - - // Visual effects — stays on main thread (just packet sends, cheap). - this._sendEffects(chunk); - - // Single combined grid scan: triggers chain reactions AND builds the - // construction snapshot in one pass, deduplicating by explosion position - // so same-location blasts only scan the grid once. - const snapshot = this._scanAndBuildSnapshot(chunk); - let plan: ExplosionPlan; - try { - plan = await this._worker.processChunk(snapshot); - } catch { - // Worker failure: fall back to synchronous processing for this chunk. - plan = this._processChunkSync(snapshot); - } - - // Apply the plan — only fast O(plan_entries) work on the main thread. - this._applyPlan(plan); - - // Batch-delete ExplosiveEntity instances from this chunk. - const explosiveIds = chunk - .filter(({ entity }) => entity instanceof ExplosiveEntity) - .map(({ entity }) => entity.characterId); - if (explosiveIds.length > 0) { - this._server.batchDeleteEntities( - explosiveIds, - this._server._explosives - ); - } - - if (i + CHUNK_SIZE < batch.length) { - await new Promise((resolve) => setImmediate(resolve)); - } - } - - // Chain reactions may have queued new explosions during _handleGridObjects. - if (this._pending.length > 0 && !this._scheduled) { - this._scheduled = true; - setImmediate(() => void this._flush()); - } - } - - /** Send particle effects for ExplosiveEntity blasts. Deduplicates on a 5m grid. */ - private _sendEffects(chunk: PendingExplosion[]): void { - const christmas = isChristmasSeason(); - const effectCells = new Set(); - for (const { entity } of chunk) { - if (!(entity instanceof ExplosiveEntity)) continue; - const pos = entity.state.position; - const key = `${Math.floor(pos[0] / 5)},${Math.floor(pos[2] / 5)}`; - if (effectCells.has(key)) continue; - effectCells.add(key); - this._server.sendCompositeEffectToAllInRange( - 600, - "", - pos, - Effects.PFX_Impact_Explosion_Landmine_Dirt_10m - ); - if (christmas) { - this._server.sendCompositeEffectToAllInRange( - 600, - "", - pos, - Effects.PFX_Seasonal_Holiday_Snow_skel - ); - } - } - } - - /** - * Single combined grid scan + snapshot builder for the whole chunk. - * - * Two separate passes (_handleGridObjects + _buildSnapshot) each called - * getGridCellsInRadius once per explosion — O(chunk_size) redundant lookups - * for same-position blasts. This merges them into one pass and deduplicates - * by rounding explosion positions to a 1m grid, so 200 IEDs at the same - * spot only scan the grid once. - */ - private _scanAndBuildSnapshot(chunk: PendingExplosion[]): ChunkSnapshot { - const server = this._server; - - // ---- Explosions (serialised for worker) ------------------------ - const explosions = chunk.map(({ entity }) => ({ - x: entity.state.position[0], - y: entity.state.position[1], - z: entity.state.position[2], - charBaseDamage: charBaseDamage(entity), - vehicleBaseDamage: vehicleBaseDamage(entity), - constructionDamage: constructionBaseDamage( - entity, - server.baseConstructionDamage - ), - attackerId: attackerId(entity), - weaponId: weaponId(entity) - })); - - // ---- Grid scan (one lookup per unique 1m position key) --------- - // Multiple explosions at the same spot produce identical grid lookups. - // Keep one representative (entity, client) per key so chain reactions - // are still triggered exactly once per nearby un-detonated IED. - const scannedKeys = new Map(); - for (const pe of chunk) { - const p = pe.entity.state.position; - const key = `${Math.round(p[0])},${Math.round(p[2])}`; - if (!scannedKeys.has(key)) scannedKeys.set(key, pe); - } - - const constructionMap = new Map(); - for (const { entity, client } of scannedKeys.values()) { - const cells = server.getGridCellsInRadius( - entity.state.position, - EXPLOSION_GRID_RADIUS - ); - for (const cell of cells) { - for (const obj of cell.objects) { - if (obj instanceof ExplosiveEntity && obj.detonated) continue; - - if (obj instanceof ConstructionChildEntity) { - if (constructionMap.has(obj.characterId)) continue; - if (IMMUNE_IDS.has(obj.itemDefinitionId)) continue; - if ( - server.constructionManager.isConstructionInSecuredArea( - server, - obj - ) - ) - continue; - const isSimple = !!server._worldSimpleConstruction[obj.characterId]; - const pos = obj.fixedPosition ?? obj.state.position; - constructionMap.set(obj.characterId, { - entityId: obj.characterId, - x: pos[0], - y: pos[1], - z: pos[2], - damageRange: obj.damageRange, - explosionRadius: isSimple ? 4 : obj.damageRange * 1.5 - }); - } else if (obj instanceof ConstructionDoor) { - if (constructionMap.has(obj.characterId)) continue; - if ( - server.constructionManager.isConstructionInSecuredArea( - server, - obj - ) - ) - continue; - constructionMap.set(obj.characterId, { - entityId: obj.characterId, - x: obj.state.position[0], - y: obj.state.position[1], - z: obj.state.position[2], - damageRange: obj.damageRange, - explosionRadius: obj.damageRange - }); - } else if ( - obj instanceof ConstructionParentEntity && - SHACK_IDS.has(obj.itemDefinitionId) - ) { - if (constructionMap.has(obj.characterId)) continue; - constructionMap.set(obj.characterId, { - entityId: obj.characterId, - x: obj.state.position[0], - y: obj.state.position[1], - z: obj.state.position[2], - damageRange: obj.damageRange, - explosionRadius: obj.damageRange * 1.5 - }); - } else { - // Chain reactions (ExplosiveEntity within 2m), traps, destroyables — - // keep on main thread since they mutate live state directly. - obj.OnExplosiveHit(server, entity, client); - } - } - } - } - - // ---- Characters (pre-filtered by extended AABB) ---------------- - let minX = Infinity, - maxX = -Infinity, - minY = Infinity, - maxY = -Infinity, - minZ = Infinity, - maxZ = -Infinity; - for (const { entity } of chunk) { - const p = entity.state.position; - if (p[0] < minX) minX = p[0]; - if (p[0] > maxX) maxX = p[0]; - if (p[1] < minY) minY = p[1]; - if (p[1] > maxY) maxY = p[1]; - if (p[2] < minZ) minZ = p[2]; - if (p[2] > maxZ) maxZ = p[2]; - } - minX -= EXPLOSION_CHARACTER_RADIUS; - maxX += EXPLOSION_CHARACTER_RADIUS; - minY -= EXPLOSION_CHARACTER_Y_RADIUS; - maxY += EXPLOSION_CHARACTER_Y_RADIUS; - minZ -= EXPLOSION_CHARACTER_RADIUS; - maxZ += EXPLOSION_CHARACTER_RADIUS; - - const characters = server.isPvE - ? [] - : Object.entries(server._characters) - .filter(([, char]) => { - if (!char.isAlive) return false; - const p = char.state.position; - return ( - p[0] >= minX && - p[0] <= maxX && - p[1] >= minY && - p[1] <= maxY && - p[2] >= minZ && - p[2] <= maxZ - ); - }) - .map(([characterId, char]) => ({ - characterId, - x: char.state.position[0], - y: char.state.position[1], - z: char.state.position[2] - })); - - // ---- Vehicles -------------------------------------------------- - const vehicles = server.isPvE - ? [] - : Object.entries(server._vehicles).map(([vehicleId, veh]) => ({ - vehicleId, - x: veh.state.position[0], - y: veh.state.position[1], - z: veh.state.position[2] - })); - - return { - explosions, - characters, - vehicles, - construction: Array.from(constructionMap.values()) - }; - } - - /** Apply the worker's damage plan — O(plan entries), no heavy computation. */ - private _applyPlan(plan: ExplosionPlan): void { - const server = this._server; - - for (const entry of plan.characterDamages) { - const char = server._characters[entry.characterId]; - if (!char?.isAlive) continue; - char.damage(server, { - entity: entry.attackerId, - weapon: entry.weapon, - damage: entry.damage - }); - } - - for (const entry of plan.vehicleDamages) { - const veh = server._vehicles[entry.vehicleId]; - if (!veh) continue; - veh.damage(server, { entity: entry.attackerId, damage: entry.damage }); - } - - for (const entry of plan.constructionDamages) { - const obj = - server._constructionSimple[entry.entityId] ?? - server._constructionFoundations[entry.entityId] ?? - server._constructionDoors[entry.entityId]; - if (!obj) continue; - obj.damage(server, { - entity: entry.attackerId, - damage: entry.totalDamage, - explosive: true - }); - } - } - - /** - * Synchronous fallback used when the worker errors. - * Mirrors the worker's processChunk logic but runs on the main thread. - */ - private _processChunkSync(snapshot: ChunkSnapshot): ExplosionPlan { - const { explosions, characters, vehicles, construction } = snapshot; - - const characterDamages = characters - .map((char) => { - let totalDamage = 0, - lastAttacker = "", - lastWeapon: number | undefined; - for (const exp of explosions) { - const dx = char.x - exp.x; - if (Math.abs(dx) > EXPLOSION_CHARACTER_RADIUS) continue; - const dz = char.z - exp.z; - if (Math.abs(dz) > EXPLOSION_CHARACTER_RADIUS) continue; - const dy = char.y - exp.y; - if (Math.abs(dy) > EXPLOSION_CHARACTER_Y_RADIUS) continue; - const d = Math.sqrt(dx * dx + dy * dy + dz * dz); - totalDamage += d > 1 ? exp.charBaseDamage / d : exp.charBaseDamage; - lastAttacker = exp.attackerId; - lastWeapon = exp.weaponId; - } - return totalDamage >= 1 && lastAttacker - ? { - characterId: char.characterId, - damage: Math.floor(totalDamage), - attackerId: lastAttacker, - weapon: lastWeapon - } - : null; - }) - .filter(Boolean) as ExplosionPlan["characterDamages"]; - - const vehicleDamages = vehicles - .map((veh) => { - let totalDamage = 0, - lastAttacker = ""; - for (const exp of explosions) { - const dx = veh.x - exp.x, - dy = veh.y - exp.y, - dz = veh.z - exp.z; - const sq = dx * dx + dy * dy + dz * dz; - if (sq > 25) continue; - const d = Math.sqrt(sq); - totalDamage += - d > 1 ? exp.vehicleBaseDamage / d : exp.vehicleBaseDamage; - lastAttacker = exp.attackerId; - } - return totalDamage >= 1 && lastAttacker - ? { - vehicleId: veh.vehicleId, - damage: Math.floor(totalDamage), - attackerId: lastAttacker - } - : null; - }) - .filter(Boolean) as ExplosionPlan["vehicleDamages"]; - - const constructionDamages: ExplosionPlan["constructionDamages"] = []; - for (const cons of construction) { - let totalDamage = 0, - lastAttacker = ""; - const rSq = cons.explosionRadius * cons.explosionRadius; - for (const exp of explosions) { - const dx = cons.x - exp.x, - dy = cons.y - exp.y, - dz = cons.z - exp.z; - const sq = dx * dx + dy * dy + dz * dz; - if (sq > rSq) continue; - const d = Math.sqrt(sq); - totalDamage += - d < cons.damageRange - ? exp.constructionDamage - : exp.constructionDamage / Math.sqrt(d); - lastAttacker = exp.attackerId; - } - if (totalDamage >= 1 && lastAttacker) - constructionDamages.push({ - entityId: cons.entityId, - totalDamage: Math.floor(totalDamage), - attackerId: lastAttacker - }); - } - - return { characterDamages, vehicleDamages, constructionDamages }; - } -} diff --git a/src/servers/ZoneServer2016/managers/explosionworker.ts b/src/servers/ZoneServer2016/managers/explosionworker.ts deleted file mode 100644 index 3679fa1810..0000000000 --- a/src/servers/ZoneServer2016/managers/explosionworker.ts +++ /dev/null @@ -1,83 +0,0 @@ -// ====================================================================== -// -// GNU GENERAL PUBLIC LICENSE -// Version 3, 29 June 2007 -// copyright (C) 2020 - 2021 Quentin Gruber -// copyright (C) 2021 - 2026 H1emu community -// -// https://github.com/QuentinGruber/h1z1-server -// https://www.npmjs.com/package/h1z1-server -// -// Based on https://github.com/psemu/soe-network -// ====================================================================== - -import path from "path"; -import { Worker } from "worker_threads"; -import type { ChunkSnapshot, ExplosionPlan } from "./explosion.worker"; - -interface PendingRequest { - resolve: (plan: ExplosionPlan) => void; - reject: (err: Error) => void; -} - -interface WorkerResponse { - requestId: number; - plan?: ExplosionPlan; - error?: string; -} - -export class ExplosionWorker { - private readonly _worker: Worker; - private _requestId = 0; - private _pending = new Map(); - private _stopped = false; - - constructor() { - const workerPath = path.join(__dirname, "explosion.worker.js"); - this._worker = new Worker(workerPath); - - this._worker.on("message", (msg: WorkerResponse) => { - const req = this._pending.get(msg.requestId); - if (!req) return; - this._pending.delete(msg.requestId); - if (msg.error) { - req.reject(new Error(msg.error)); - } else { - req.resolve(msg.plan!); - } - }); - - this._worker.on("error", (err: Error) => this._rejectAll(err)); - this._worker.on("exit", (code) => { - this._stopped = true; - if (code !== 0) - this._rejectAll(new Error(`explosion worker exited ${code}`)); - }); - } - - processChunk(snapshot: ChunkSnapshot): Promise { - if (this._stopped) { - return Promise.resolve({ - characterDamages: [], - vehicleDamages: [], - constructionDamages: [] - }); - } - const requestId = this._requestId++; - return new Promise((resolve, reject) => { - this._pending.set(requestId, { resolve, reject }); - this._worker.postMessage({ requestId, snapshot }); - }); - } - - async stop(): Promise { - this._stopped = true; - this._rejectAll(new Error("explosion worker stopped")); - await this._worker.terminate(); - } - - private _rejectAll(err: Error): void { - this._pending.forEach((req) => req.reject(err)); - this._pending.clear(); - } -} diff --git a/src/servers/ZoneServer2016/managers/fairplaymanager.ts b/src/servers/ZoneServer2016/managers/fairplaymanager.ts index 2c6b0a5dd1..9f7172c15c 100644 --- a/src/servers/ZoneServer2016/managers/fairplaymanager.ts +++ b/src/servers/ZoneServer2016/managers/fairplaymanager.ts @@ -44,17 +44,10 @@ import { } from "../models/enums"; import { ZoneServer2016 } from "../zoneserver"; import { FileHash } from "types/shared"; -import { PluginManager } from "./pluginmanager"; -const encryptedData = PluginManager.loadServerData( - "2016/encryptedData/encryptedData.json" - ), - fairPlayData = PluginManager.loadServerData( - "2016/encryptedData/fairPlayData.json" - ), - defaultHashes: Array = PluginManager.loadServerData( - "2016/dataSources/AllowedFileHashes.json" - ); +const encryptedData = require("../../../../data/2016/encryptedData/encryptedData.json"), + fairPlayData = require("../../../../data/2016/encryptedData/fairPlayData.json"), + defaultHashes: Array = require("../../../../data/2016/dataSources/AllowedFileHashes.json"); export class FairPlayManager { _decryptKey: string = ""; @@ -154,32 +147,19 @@ export class FairPlayManager { position[1] - client.startLoc > this.fairPlayValues.maxFlying ) { let kick = true; - // Cache the foundation check for 2.5s to avoid scanning all foundations every movement packet - const FOUNDATION_CACHE_TTL = 2500; - if ( - Date.now() - client.fairPlayFoundationCheckTime > - FOUNDATION_CACHE_TTL - ) { - let insideFoundation = false; - for (const a in server._constructionFoundations) { - if ( - server._constructionFoundations[a].getHasPermission( - server, - client.character.characterId, - ConstructionPermissionIds.VISIT - ) && - server._constructionFoundations[a].isInside( - client.character.state.position - ) - ) { - insideFoundation = true; - break; - } - } - client.fairPlayFoundationCheckResult = insideFoundation; - client.fairPlayFoundationCheckTime = Date.now(); + for (const a in server._constructionFoundations) { + if ( + server._constructionFoundations[a].getHasPermission( + server, + client.character.characterId, + ConstructionPermissionIds.VISIT + ) && + server._constructionFoundations[a].isInside( + client.character.state.position + ) + ) + kick = false; } - if (client.fairPlayFoundationCheckResult) kick = false; for (const char in server._characters) { if ( server._characters[char].characterId == client.character.characterId @@ -192,10 +172,8 @@ export class FairPlayManager { server._characters[char].state.position, 4.5 ) - ) { + ) kick = false; - break; - } } if (kick) { server.kickPlayer(client); @@ -789,20 +767,18 @@ export class FairPlayManager { // check if all default / required packs are found in game files for (const serverValue of hashes) { if (!serverValue) continue; + let received: FileHash | undefined; if ( - receivedHashes.find((clientValue) => - this.validateFile(serverValue, clientValue) - ) + receivedHashes.find((clientValue) => { + received = clientValue; + return this.validateFile(serverValue, clientValue); + }) ) { validatedHashes.push(serverValue); continue; } - // find by name only to report what hash the client actually sent - const received = receivedHashes.find( - (clientValue) => clientValue.file_name === serverValue.file_name - ); console.log( - `${client.loginSessionId} (${client.character.name}) failed asset integrity check due to missing or invalid file ${serverValue.file_name} received: ${received?.crc32_hash ?? "missing"} expected: ${serverValue.crc32_hash}` + `${client.loginSessionId} (${client.character.name}) failed asset integrity check due to missing or invalid file ${serverValue.file_name} received: ${received?.crc32_hash} expected: ${serverValue.crc32_hash}` ); server.kickPlayerWithReason( client, diff --git a/src/servers/ZoneServer2016/managers/lootspawn.worker.ts b/src/servers/ZoneServer2016/managers/lootspawn.worker.ts deleted file mode 100644 index 9c41f5cb3d..0000000000 --- a/src/servers/ZoneServer2016/managers/lootspawn.worker.ts +++ /dev/null @@ -1,615 +0,0 @@ -import { parentPort, workerData } from "node:worker_threads"; -import { PluginManager } from "./pluginmanager"; -import type { - GroundLootTableJson, - ContainerLootTableJson, - LootCondition, - LootPool, - LootTableEntry, - ItemFunction -} from "types/zoneserver"; - -const Z1_items = PluginManager.loadServerData("2016/zoneData/Z1_items.json"); -const Z1_npcs = PluginManager.loadServerData("2016/zoneData/Z1_npcs.json"); -const Z1_POIs = PluginManager.loadServerData("2016/zoneData/Z1_POIs.json"); - -const { groundTables, containerTables } = workerData as { - groundTables: Record; - containerTables: Record; -}; - -interface SpawnedItemSnapshot { - position: [number, number, number]; - itemDefinitionId: number; -} - -interface LootPlanRequest { - requestId: number; - type: "loot"; - payload: { - spawnedLootSpawnerIds: number[]; - spawnedItems: SpawnedItemSnapshot[]; - ingameHour: number; - }; -} - -interface ContainerPropSnapshot { - characterId: string; - lootSpawner: string; - shouldSpawnLoot: boolean; - position: number[]; - existingItemDefinitionIds: number[]; -} - -interface ContainerPlanRequest { - requestId: number; - type: "container"; - payload: { - props: ContainerPropSnapshot[]; - }; -} - -interface NpcPlanRequest { - requestId: number; - type: "npcs"; - payload: { - existingNpcPositions: number[][]; - npcSpawnRadius: number; - chanceNpc: number; - chanceScreamer: number; - }; -} - -interface DespawnRequest { - requestId: number; - type: "despawn"; - payload: { - now: number; - deadNpcDespawnTimer: number; - lootbagDespawnTimer: number; - itemDespawnTimer: number; - lootDespawnTimer: number; - fuelItemDefinitionIds: number[]; - npcs: Array<{ - characterId: string; - knockedOut: boolean; - deathTime: number; - }>; - lootbags: Array<{ - characterId: string; - creationTime: number; - }>; - items: Array<{ - characterId: string; - creationTime: number; - spawnerId: number; - itemDefinitionId: number; - }>; - }; -} - -type WorkerRequest = - | LootPlanRequest - | ContainerPlanRequest - | NpcPlanRequest - | DespawnRequest; - -interface LootPlanEntry { - spawnerId: number; - itemDefinitionId: number; - count: number; - position: number[]; - rotation: number[]; - functions?: ItemFunction[]; -} - -interface ContainerPlanEntry { - characterId: string; - itemDefinitionId: number; - count: number; - functions?: ItemFunction[]; -} - -interface ResolvedEntry { - itemDefinitionId: number; - count: number; - functions?: ItemFunction[]; -} - -interface NpcPlanEntry { - spawnerId: number; - modelId: number; - position: number[]; - rotation: number[]; -} - -interface DespawnPlan { - npcCharacterIds: string[]; - lootbagCharacterIds: string[]; - itemEntries: Array<{ - characterId: string; - spawnerId: number; - itemDefinitionId: number; - deleteExplosive: boolean; - }>; -} - -interface WorkerResponse { - requestId: number; - type: "loot" | "container" | "npcs" | "despawn"; - plan?: LootPlanEntry[] | ContainerPlanEntry[] | NpcPlanEntry[] | DespawnPlan; - error?: string; -} - -// ── Geometry helpers ────────────────────────────────────────────────────────── - -function getAuthorizedNpcModels(actorDefinition: string): number[] { - switch (actorDefinition) { - case "NPCSpawner_ZombieLazy.adr": - case "NPCSpawner_ZombieWalker.adr": - return [9510, 9634]; - case "NPCSpawner_Deer001.adr": - return [9002, 9253]; - case "NPCSpawner_Wolf001.adr": - return [9003]; - case "Bear_Brown.adr": - return [9187]; - default: - return []; - } -} - -function isPosInRadius( - radius: number, - position1: number[], - position2: number[] -): boolean { - const x = position1[0] - position2[0]; - const y = position1[1] - position2[1]; - const z = position1[2] - position2[2]; - return Math.sqrt(x * x + y * y + z * z) <= radius; -} - -function isPosInRadius2D( - radius: number, - position1: number[], - position2: number[] -): boolean { - const x = position1[0] - position2[0]; - const z = position1[2] - position2[2]; - return Math.sqrt(x * x + z * z) <= radius; -} - -function isInsidePolygon(point: [number, number], vs: number[][]): boolean { - const x = point[0]; - const y = point[1]; - let inside = false; - for (let i = 0, j = vs.length - 1; i < vs.length; j = i++) { - const xi = vs[i][0]; - const yi = vs[i][1]; - const xj = vs[j][0]; - const yj = vs[j][1]; - const intersect = - yi > y != yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi; - if (intersect) inside = !inside; - } - return inside; -} - -/** Returns true if the XZ position is inside a POI's polygon bounds or radius. */ -function isPosInPoi(position: number[], poi: any): boolean { - if (poi.bounds) { - for (const bound of poi.bounds) { - if (isInsidePolygon([position[0], position[2]], bound)) return true; - } - return false; - } - if (poi.range && poi.position) { - return isPosInRadius2D(poi.range, position, poi.position); - } - return false; -} - -// ── Condition evaluation ────────────────────────────────────────────────────── - -interface LootContext { - position: number[]; - spawnedItems: SpawnedItemSnapshot[]; - ingameHour: number; -} - -function evaluateConditions( - conditions: LootCondition[], - ctx: LootContext -): boolean { - for (const cond of conditions) { - switch (cond.condition) { - case "in_poi": { - const match = Z1_POIs.some((poi: any) => { - if (cond.poi_ids && !cond.poi_ids.includes(poi.POIid)) return false; - if (cond.poi_names && !cond.poi_names.includes(poi.POIname)) - return false; - return isPosInPoi(ctx.position, poi); - }); - if (!match) return false; - break; - } - - case "not_in_poi": { - const anyPoi = Z1_POIs.some((poi: any) => - isPosInPoi(ctx.position, poi) - ); - if (anyPoi) return false; - break; - } - - case "poi_tag": { - const match = Z1_POIs.some( - (poi: any) => - Array.isArray(poi.tags) && - (cond.tags ?? []).some((tag: string) => poi.tags.includes(tag)) && - isPosInPoi(ctx.position, poi) - ); - if (!match) return false; - break; - } - - case "not_poi_tag": { - // Skip pool if the position is inside ANY POI that has one of the given tags - const inTaggedPoi = Z1_POIs.some( - (poi: any) => - Array.isArray(poi.tags) && - (cond.tags ?? []).some((tag: string) => poi.tags.includes(tag)) && - isPosInPoi(ctx.position, poi) - ); - if (inTaggedPoi) return false; - break; - } - - case "random_chance": { - if (Math.random() * 100 > (cond.chance ?? 100)) return false; - break; - } - - case "elevation_range": { - const y = ctx.position[1]; - if (cond.min !== undefined && y < cond.min) return false; - if (cond.max !== undefined && y > cond.max) return false; - break; - } - - case "item_density": { - const ids = new Set(cond.item_ids ?? []); - const radius = cond.radius ?? 50; - const maxCount = cond.max_count ?? 1; - let count = 0; - for (const item of ctx.spawnedItems) { - if (!ids.has(item.itemDefinitionId)) continue; - if (isPosInRadius2D(radius, ctx.position, item.position)) { - count++; - if (count >= maxCount) break; - } - } - if (count >= maxCount) return false; - break; - } - - case "server_time": { - const hour = ctx.ingameHour; - const min = cond.hour_min ?? 0; - const max = cond.hour_max ?? 23; - // Support wrap-around (e.g. 22–04) - const active = - min <= max ? hour >= min && hour <= max : hour >= min || hour <= max; - if (!active) return false; - break; - } - } - } - return true; -} - -/** - * Returns entries from all pools whose conditions pass for the given context. - * Pools with empty conditions arrays always pass. - */ -function getEligibleEntries( - pools: LootPool[], - ctx: LootContext -): LootTableEntry[] { - const entries: LootTableEntry[] = []; - for (const pool of pools) { - if (evaluateConditions(pool.conditions, ctx)) { - entries.push(...pool.entries); - } - } - return entries; -} - -function getRandomItem(items: LootTableEntry[]): LootTableEntry | undefined { - const totalWeight = items.reduce((total, item) => total + item.weight, 0); - const randomWeight = Math.random() * totalWeight; - let currentWeight = 0; - for (let i = 0; i < items.length; i++) { - currentWeight += items[i].weight; - if (currentWeight > randomWeight) return items[i]; - } - return undefined; -} - -/** - * Resolves a raw LootTableEntry to a concrete item, handling entry types: - * - "item" (default): returns the item directly. - * - "loot_table": recursively draws from the referenced table. - * - "empty": returns null (no item spawns). - * Depth-limited to 5 to prevent infinite cycles. - */ -function resolveEntry( - entry: LootTableEntry | undefined, - ctx: LootContext, - depth = 0 -): ResolvedEntry | null { - if (!entry || depth > 5) return null; - const type = entry.type ?? "item"; - - if (type === "empty") return null; - - if (type === "loot_table") { - const table = - (containerTables[entry.table ?? ""] as - | { pools: LootPool[] } - | undefined) ?? - (groundTables[entry.table ?? ""] as { pools: LootPool[] } | undefined); - if (!table) return null; - const eligible = getEligibleEntries(table.pools, ctx); - return resolveEntry(getRandomItem(eligible), ctx, depth + 1); - } - - // type === "item" - if (entry.item === undefined) return null; - const count = entry.count - ? Math.floor( - Math.random() * (entry.count.max - entry.count.min + 1) + - entry.count.min - ) - : 1; - return { itemDefinitionId: entry.item, count, functions: entry.functions }; -} - -// ── Loot plan ──────────────────────────────────────────────────────────────── - -function createLootPlan( - spawnedLootSpawnerIds: number[], - spawnedItems: SpawnedItemSnapshot[], - ingameHour: number -): LootPlanEntry[] { - const spawnedSet = new Set(spawnedLootSpawnerIds); - const plan: LootPlanEntry[] = []; - - for (const spawnerType of Z1_items) { - const lootTable = groundTables[spawnerType.actorDefinition]; - if (!lootTable) continue; - - for (const itemInstance of spawnerType.instances) { - if (spawnedSet.has(itemInstance.id)) continue; - - const chance = Math.floor(Math.random() * 100) + 1; - if (chance > lootTable.spawnChance) continue; - - const ctx: LootContext = { - position: itemInstance.position, - spawnedItems, - ingameHour - }; - - const entries = getEligibleEntries(lootTable.pools, ctx); - if (!entries.length) continue; - - const resolved = resolveEntry(getRandomItem(entries), ctx); - if (!resolved) continue; - - plan.push({ - spawnerId: itemInstance.id, - itemDefinitionId: resolved.itemDefinitionId, - count: resolved.count, - position: itemInstance.position, - rotation: itemInstance.rotation, - functions: resolved.functions - }); - } - } - - return plan; -} - -// ── Container loot plan ─────────────────────────────────────────────────────── - -function createContainerLootPlan( - props: ContainerPropSnapshot[] -): ContainerPlanEntry[] { - const plan: ContainerPlanEntry[] = []; - - for (const prop of props) { - if (!prop.shouldSpawnLoot) continue; - if (prop.existingItemDefinitionIds.length > 0) continue; - - const lootTable = containerTables[prop.lootSpawner]; - if (!lootTable) continue; - - // Containers don't use item_density / server_time / loot_tier by default, - // but they can — pass neutral values so conditions still evaluate correctly. - const ctx: LootContext = { - position: prop.position, - spawnedItems: [], - ingameHour: 12 - }; - - const containerItemIds = new Set(prop.existingItemDefinitionIds); - - for (const pool of lootTable.pools) { - if (!evaluateConditions(pool.conditions, ctx)) continue; - - const rolls = pool.rolls - ? Math.floor( - Math.random() * (pool.rolls.max - pool.rolls.min + 1) + - pool.rolls.min - ) - : 1; - - for (let r = 0; r < rolls; r++) { - const resolved = resolveEntry(getRandomItem(pool.entries), ctx); - if (!resolved) continue; - if (containerItemIds.has(resolved.itemDefinitionId)) continue; - plan.push({ - characterId: prop.characterId, - itemDefinitionId: resolved.itemDefinitionId, - count: resolved.count, - functions: resolved.functions - }); - containerItemIds.add(resolved.itemDefinitionId); - } - } - } - - return plan; -} - -// ── NPC plan ────────────────────────────────────────────────────────────────── - -function createNpcPlan( - existingNpcPositions: number[][], - npcSpawnRadius: number, - chanceNpc: number, - chanceScreamer: number -): NpcPlanEntry[] { - const plan: NpcPlanEntry[] = []; - const plannedPositions: number[][] = [...existingNpcPositions]; - - for (const spawnerType of Z1_npcs) { - const baseModels = getAuthorizedNpcModels(spawnerType.actorDefinition); - if (!baseModels.length) continue; - - for (const npcInstance of spawnerType.instances) { - let blocked = false; - for (const npcPos of plannedPositions) { - if (isPosInRadius(npcSpawnRadius, npcInstance.position, npcPos)) { - blocked = true; - break; - } - } - if (blocked) continue; - - const spawnChanceRoll = Math.floor(Math.random() * 100) + 1; - if (spawnChanceRoll > chanceNpc) continue; - - const models = [...baseModels]; - const screamerChanceRoll = Math.floor(Math.random() * 1000) + 1; - if (screamerChanceRoll <= chanceScreamer) models.push(9667); - - const modelId = models[Math.floor(Math.random() * models.length)]; - plan.push({ - spawnerId: npcInstance.id, - modelId, - position: npcInstance.position, - rotation: npcInstance.rotation - }); - plannedPositions.push(npcInstance.position); - } - } - - return plan; -} - -// ── Despawn plan ────────────────────────────────────────────────────────────── - -function createDespawnPlan(payload: DespawnRequest["payload"]): DespawnPlan { - const fuelItemDefinitionIds = new Set(payload.fuelItemDefinitionIds); - - return { - npcCharacterIds: payload.npcs - .filter( - (npc) => - npc.knockedOut && - payload.now - npc.deathTime >= payload.deadNpcDespawnTimer - ) - .map((npc) => npc.characterId), - lootbagCharacterIds: payload.lootbags - .filter( - (lootbag) => - payload.now - lootbag.creationTime >= payload.lootbagDespawnTimer - ) - .map((lootbag) => lootbag.characterId), - itemEntries: payload.items - .filter((item) => { - const despawnTime = - item.spawnerId === -1 - ? payload.itemDespawnTimer - : payload.lootDespawnTimer; - return payload.now - item.creationTime >= despawnTime; - }) - .map((item) => ({ - characterId: item.characterId, - spawnerId: item.spawnerId, - itemDefinitionId: item.itemDefinitionId, - deleteExplosive: fuelItemDefinitionIds.has(item.itemDefinitionId) - })) - }; -} - -// ── Message handler ─────────────────────────────────────────────────────────── - -parentPort?.on("message", (request: WorkerRequest) => { - try { - if (request.type === "loot") { - const plan = createLootPlan( - request.payload.spawnedLootSpawnerIds, - request.payload.spawnedItems, - request.payload.ingameHour - ); - parentPort?.postMessage({ - requestId: request.requestId, - type: "loot", - plan - } as WorkerResponse); - return; - } - - if (request.type === "npcs") { - const plan = createNpcPlan( - request.payload.existingNpcPositions, - request.payload.npcSpawnRadius, - request.payload.chanceNpc, - request.payload.chanceScreamer - ); - parentPort?.postMessage({ - requestId: request.requestId, - type: "npcs", - plan - } as WorkerResponse); - return; - } - - if (request.type === "despawn") { - const plan = createDespawnPlan(request.payload); - parentPort?.postMessage({ - requestId: request.requestId, - type: "despawn", - plan - } as WorkerResponse); - return; - } - - const plan = createContainerLootPlan(request.payload.props); - parentPort?.postMessage({ - requestId: request.requestId, - type: "container", - plan - } as WorkerResponse); - } catch (error) { - parentPort?.postMessage({ - requestId: request.requestId, - type: request.type, - error: String(error) - } as WorkerResponse); - } -}); diff --git a/src/servers/ZoneServer2016/managers/lootspawnworker.ts b/src/servers/ZoneServer2016/managers/lootspawnworker.ts deleted file mode 100644 index a6f0325696..0000000000 --- a/src/servers/ZoneServer2016/managers/lootspawnworker.ts +++ /dev/null @@ -1,258 +0,0 @@ -import path from "node:path"; -import { Worker } from "node:worker_threads"; - -interface LootPlanEntry { - spawnerId: number; - itemDefinitionId: number; - count: number; - position: number[]; - rotation: number[]; - functions?: import("types/zoneserver").ItemFunction[]; -} - -interface NpcPlanEntry { - spawnerId: number; - modelId: number; - position: number[]; - rotation: number[]; -} - -export interface NpcDespawnSnapshot { - characterId: string; - knockedOut: boolean; - deathTime: number; -} - -export interface LootbagDespawnSnapshot { - characterId: string; - creationTime: number; -} - -export interface ItemDespawnSnapshot { - characterId: string; - creationTime: number; - spawnerId: number; - itemDefinitionId: number; -} - -interface DespawnItemPlanEntry { - characterId: string; - spawnerId: number; - itemDefinitionId: number; - deleteExplosive: boolean; -} - -interface DespawnPlan { - npcCharacterIds: string[]; - lootbagCharacterIds: string[]; - itemEntries: DespawnItemPlanEntry[]; -} - -export interface ContainerPropSnapshot { - characterId: string; - lootSpawner: string; - shouldSpawnLoot: boolean; - position: number[]; - existingItemDefinitionIds: number[]; -} - -export interface SpawnedItemSnapshot { - position: [number, number, number]; - itemDefinitionId: number; -} - -interface ContainerPlanEntry { - characterId: string; - itemDefinitionId: number; - count: number; - functions?: import("types/zoneserver").ItemFunction[]; -} - -type WorkerRequest = - | { - requestId: number; - type: "loot"; - payload: { - spawnedLootSpawnerIds: number[]; - spawnedItems: SpawnedItemSnapshot[]; - ingameHour: number; - }; - } - | { - requestId: number; - type: "container"; - payload: { - props: ContainerPropSnapshot[]; - }; - } - | { - requestId: number; - type: "npcs"; - payload: { - existingNpcPositions: number[][]; - npcSpawnRadius: number; - chanceNpc: number; - chanceScreamer: number; - }; - } - | { - requestId: number; - type: "despawn"; - payload: { - now: number; - deadNpcDespawnTimer: number; - lootbagDespawnTimer: number; - itemDespawnTimer: number; - lootDespawnTimer: number; - fuelItemDefinitionIds: number[]; - npcs: NpcDespawnSnapshot[]; - lootbags: LootbagDespawnSnapshot[]; - items: ItemDespawnSnapshot[]; - }; - }; - -interface WorkerResponse { - requestId: number; - type: "loot" | "container" | "npcs" | "despawn"; - plan?: LootPlanEntry[] | ContainerPlanEntry[] | NpcPlanEntry[] | DespawnPlan; - error?: string; -} - -interface PendingRequest { - resolve: (value: any) => void; - reject: (reason?: unknown) => void; -} - -export class LootSpawnWorker { - private readonly worker: Worker; - private requestId = 0; - private pendingRequests: Map = new Map(); - private isStopped = false; - - constructor(workerData: { - groundTables: Record; - containerTables: Record; - }) { - const workerPath = path.join(__dirname, "lootspawn.worker.js"); - this.worker = new Worker(workerPath, { workerData }); - - this.worker.on("message", (message: WorkerResponse) => { - const pendingRequest = this.pendingRequests.get(message.requestId); - if (!pendingRequest) return; - - this.pendingRequests.delete(message.requestId); - if (message.error) { - pendingRequest.reject(new Error(message.error)); - return; - } - pendingRequest.resolve(message.plan ?? []); - }); - - this.worker.on("error", (error) => { - this.rejectAllPending(error); - }); - - this.worker.on("exit", (code) => { - this.isStopped = true; - if (code !== 0) { - this.rejectAllPending( - new Error(`Loot spawn worker exited with code ${code}`) - ); - } - }); - } - - createLootPlan( - spawnedLootSpawnerIds: number[], - spawnedItems: SpawnedItemSnapshot[], - ingameHour: number - ): Promise { - const request: WorkerRequest = { - requestId: this.requestId++, - type: "loot", - payload: { - spawnedLootSpawnerIds, - spawnedItems, - ingameHour - } - }; - return this.request(request); - } - - createContainerLootPlan( - props: ContainerPropSnapshot[] - ): Promise { - const request: WorkerRequest = { - requestId: this.requestId++, - type: "container", - payload: { - props - } - }; - return this.request(request); - } - - createNpcPlan( - existingNpcPositions: number[][], - npcSpawnRadius: number, - chanceNpc: number, - chanceScreamer: number - ): Promise { - const request: WorkerRequest = { - requestId: this.requestId++, - type: "npcs", - payload: { - existingNpcPositions, - npcSpawnRadius, - chanceNpc, - chanceScreamer - } - }; - return this.request(request); - } - - createDespawnPlan(payload: { - now: number; - deadNpcDespawnTimer: number; - lootbagDespawnTimer: number; - itemDespawnTimer: number; - lootDespawnTimer: number; - fuelItemDefinitionIds: number[]; - npcs: NpcDespawnSnapshot[]; - lootbags: LootbagDespawnSnapshot[]; - items: ItemDespawnSnapshot[]; - }): Promise { - const request: WorkerRequest = { - requestId: this.requestId++, - type: "despawn", - payload - }; - return this.request(request); - } - - async stop(): Promise { - if (this.isStopped) return; - this.isStopped = true; - this.rejectAllPending(new Error("Loot spawn worker stopped")); - await this.worker.terminate(); - } - - private request(request: WorkerRequest): Promise { - if (this.isStopped) return Promise.resolve([] as T); - - return new Promise((resolve, reject) => { - this.pendingRequests.set(request.requestId, { - resolve, - reject - }); - this.worker.postMessage(request); - }); - } - - private rejectAllPending(error: unknown): void { - this.pendingRequests.forEach((pendingRequest) => - pendingRequest.reject(error) - ); - this.pendingRequests.clear(); - } -} diff --git a/src/servers/ZoneServer2016/managers/loottablemanager.ts b/src/servers/ZoneServer2016/managers/loottablemanager.ts deleted file mode 100644 index cdc3fdc82c..0000000000 --- a/src/servers/ZoneServer2016/managers/loottablemanager.ts +++ /dev/null @@ -1,168 +0,0 @@ -// ====================================================================== -// -// GNU GENERAL PUBLIC LICENSE -// Version 3, 29 June 2007 -// copyright (C) 2020 - 2021 Quentin Gruber -// copyright (C) 2021 - 2026 H1emu community -// -// https://github.com/QuentinGruber/h1z1-server -// https://www.npmjs.com/package/h1z1-server -// -// Based on https://github.com/psemu/soe-network -// ====================================================================== - -import * as fs from "fs"; -import * as path from "path"; -import { PluginManager } from "./pluginmanager"; -import type { - GroundLootTableJson, - ContainerLootTableJson, - LootPool -} from "types/zoneserver"; - -export class LootTableManager { - private groundTables: Record = {}; - private containerTables: Record = {}; - - /** - * Load all loot tables from data/2016/lootTables/, applying any plugin - * overrides found in plugins//data/2016/lootTables/. - */ - load(): void { - const pluginDataRoots = PluginManager.getPluginDataRoots(); - this.groundTables = this.loadDirectory( - "ground", - pluginDataRoots - ); - this.containerTables = this.loadDirectory( - "containers", - pluginDataRoots - ); - console.log( - `[LootTableManager] Loaded ${Object.keys(this.groundTables).length} ground tables, ` + - `${Object.keys(this.containerTables).length} container tables` - ); - } - - getGroundTables(): Record { - return this.groundTables; - } - - getContainerTables(): Record { - return this.containerTables; - } - - private loadDirectory( - subdir: string, - pluginDataRoots: string[] - ): Record { - const result: Record = {}; - const baseDir = path.join( - process.cwd(), - "data", - "2016", - "lootTables", - subdir - ); - - // Collect all known table names from the base directory including subdirectories - const allNames = new Set(); - if (fs.existsSync(baseDir)) { - this.collectTableNames(baseDir, baseDir, allNames); - } - - // Plugins can also introduce brand-new tables by adding files not present in base - for (const pluginDataRoot of pluginDataRoots) { - const pluginSubDir = path.join( - pluginDataRoot, - "2016", - "lootTables", - subdir - ); - if (!fs.existsSync(pluginSubDir)) continue; - this.collectTableNames(pluginSubDir, pluginSubDir, allNames); - } - - for (const name of allNames) { - const table = this.resolveTable(subdir, name, pluginDataRoots); - if (table) result[name] = table; - } - - return result; - } - - private collectTableNames( - dir: string, - rootDir: string, - names: Set - ): void { - for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { - const fullPath = path.join(dir, entry.name); - if (entry.isDirectory()) { - this.collectTableNames(fullPath, rootDir, names); - } else if (entry.name.endsWith(".json")) { - const rel = path.relative(rootDir, fullPath); - names.add(rel.slice(0, -5).replace(/\\/g, "/")); - } - } - } - - private resolveTable( - subdir: string, - name: string, - pluginDataRoots: string[] - ): T | null { - const relPath = path.join("2016", "lootTables", subdir, `${name}.json`); - const baseFilePath = path.join(process.cwd(), "data", relPath); - - // Collect plugin overrides in priority order (last one wins for full replace, - // all appended pools are collected for append merges) - const appendPools: LootPool[] = []; - let pluginReplace: T | null = null; - - for (const pluginDataRoot of pluginDataRoots) { - const pluginFile = path.join(pluginDataRoot, relPath); - if (!fs.existsSync(pluginFile)) continue; - - const override = JSON.parse(fs.readFileSync(pluginFile, "utf8")) as T & { - merge?: string; - }; - - if (override.merge === "append") { - // Collect all pools from append-mode overrides - appendPools.push(...(override.pools ?? [])); - } else { - // Full replace — later plugin wins - const { merge: _merge, ...rest } = override as any; - pluginReplace = rest as T; - } - } - - // Load the base table if it exists - let baseTable: T | null = null; - if (fs.existsSync(baseFilePath)) { - baseTable = JSON.parse(fs.readFileSync(baseFilePath, "utf8")) as T; - } - - // Determine final table - let finalTable: T | null; - if (pluginReplace) { - // Full replace wins over base; append pools still stack on top - finalTable = pluginReplace; - } else { - finalTable = baseTable; - } - - if (!finalTable) return null; - - // Append any collected pools - if (appendPools.length > 0) { - finalTable = { - ...finalTable, - pools: [...finalTable.pools, ...appendPools] - } as T; - } - - return finalTable; - } -} diff --git a/src/servers/ZoneServer2016/managers/packetencodingworker.ts b/src/servers/ZoneServer2016/managers/packetencodingworker.ts deleted file mode 100644 index 49fe0ff11b..0000000000 --- a/src/servers/ZoneServer2016/managers/packetencodingworker.ts +++ /dev/null @@ -1,108 +0,0 @@ -import path from "node:path"; -import { Worker } from "node:worker_threads"; -import { h1z1PacketsType2016 } from "types/packets"; - -interface PacketEncodeRequest { - requestId: number; - packetName: string; - payload: unknown; -} - -interface PacketEncodeResponse { - requestId: number; - encoded?: SharedArrayBuffer; - encodedLength?: number; - isNull?: boolean; - error?: string; -} - -interface PendingRequest { - resolve: (value: Buffer | null) => void; - reject: (reason?: unknown) => void; -} - -export class PacketEncodingWorker { - private readonly worker: Worker; - private requestId = 0; - private pendingRequests: Map = new Map(); - private isStopped = false; - - constructor(protocolName: string) { - const workerPath = path.join(__dirname, "packetencoding.worker.js"); - this.worker = new Worker(workerPath, { - workerData: { - protocolName - } - }); - - this.worker.on("message", (message: PacketEncodeResponse) => { - const pendingRequest = this.pendingRequests.get(message.requestId); - if (!pendingRequest) return; - - this.pendingRequests.delete(message.requestId); - if (message.error) { - pendingRequest.reject(new Error(message.error)); - return; - } - - if (message.isNull || !message.encoded || !message.encodedLength) { - pendingRequest.resolve(null); - return; - } - - pendingRequest.resolve( - Buffer.from(message.encoded, 0, message.encodedLength) - ); - }); - - this.worker.on("error", (error) => { - this.rejectAllPending(error); - }); - - this.worker.on("exit", (code) => { - this.isStopped = true; - if (code !== 0) { - this.rejectAllPending( - new Error(`Packet encoding worker exited with code ${code}`) - ); - } - }); - } - - encodePacket( - packetName: h1z1PacketsType2016, - payload: ZonePacket - ): Promise { - if (this.isStopped) return Promise.resolve(null); - - const requestId = this.requestId++; - const request: PacketEncodeRequest = { - requestId, - packetName, - payload - }; - - return new Promise((resolve, reject) => { - this.pendingRequests.set(requestId, { - resolve, - reject - }); - this.worker.postMessage(request); - }); - } - - async stop(): Promise { - if (this.isStopped) return; - - this.isStopped = true; - this.rejectAllPending(new Error("Packet encoding worker stopped")); - await this.worker.terminate(); - } - - private rejectAllPending(error: unknown): void { - this.pendingRequests.forEach((pendingRequest) => - pendingRequest.reject(error) - ); - this.pendingRequests.clear(); - } -} diff --git a/src/servers/ZoneServer2016/managers/pluginmanager.test.ts b/src/servers/ZoneServer2016/managers/pluginmanager.test.ts index 9fb0d7362a..a561999902 100644 --- a/src/servers/ZoneServer2016/managers/pluginmanager.test.ts +++ b/src/servers/ZoneServer2016/managers/pluginmanager.test.ts @@ -18,11 +18,15 @@ import { ZoneServer2016 } from "../zoneserver"; process.env.FORCE_DISABLE_WS = "true"; // This way plugins are only enabled for this test delete process.env.DISABLE_PLUGINS; -test("PluginManager", { timeout: 60000 }, async (t) => { +test("PluginManager", { timeout: 10000 }, async (t) => { const zone = new ZoneServer2016(0); await t.test("Plugin Load", async () => { await zone.start(); - assert.ok(zone.pluginManager.pluginCount >= 1, "Test Plugin didn't load"); + assert.strictEqual( + zone.pluginManager.pluginCount, + 1, + "Test Plugin didn't load" + ); }); }); diff --git a/src/servers/ZoneServer2016/managers/pluginmanager.ts b/src/servers/ZoneServer2016/managers/pluginmanager.ts index c9ce1ee566..f2f086b7ba 100644 --- a/src/servers/ZoneServer2016/managers/pluginmanager.ts +++ b/src/servers/ZoneServer2016/managers/pluginmanager.ts @@ -13,12 +13,11 @@ import * as fs from "fs"; import * as path from "path"; -import * as yaml from "js-yaml"; -import type { ZoneServer2016 } from "../zoneserver"; -import type { Command } from "../handlers/commands/types"; -import type { ZoneClient2016 } from "../classes/zoneclient"; -import type { LoginServer } from "servers/LoginServer/loginserver"; -import { spawn } from "child_process"; +import { ZoneServer2016 } from "../zoneserver"; +import { execSync } from "child_process"; +import { copyFile, fileExists, flhash } from "../../../utils/utils"; +import { Command } from "../handlers/commands/types"; +import { ZoneClient2016 } from "../classes/zoneclient"; /** * Abstract class representing a base plugin. @@ -38,16 +37,10 @@ export abstract class BasePlugin { * @param server - The ZoneServer2016 instance. * @returns A promise that resolves when the initialization is complete. */ - public abstract init(server: ZoneServer2016 | LoginServer): Promise; + public abstract init(server: ZoneServer2016): Promise; public dir!: string; } -function isZoneServer( - server: ZoneServer2016 | LoginServer -): server is ZoneServer2016 { - return "commandHandler" in server; -} - /** * Checks if a folder exists at the specified path. * @param path - The path of the folder to check. @@ -59,8 +52,8 @@ function folderExists(path: string): boolean { const stats = fs.statSync(path); return stats.isDirectory(); } catch (err: any) { - if (err.code === "ENOENT" || err.code === "ENOTDIR") { - // Folder doesn't exist or path traverses through a non-directory + if (err.code === "ENOENT") { + // Folder doesn't exist return false; } else { // Other error occurred @@ -150,161 +143,11 @@ function replaceInFile( fs.writeFileSync(filePath, replacedContent, "utf8"); } -function fileExists(filePath: string): boolean { - try { - fs.accessSync(filePath); - return true; - } catch { - return false; - } -} - -async function copyFile( - originalFilePath: string, - newFilePath: string -): Promise { - return new Promise((resolve, reject) => { - const readStream = fs.createReadStream(originalFilePath); - const writeStream = fs.createWriteStream(newFilePath); - - readStream.pipe(writeStream); - - writeStream.on("finish", () => { - readStream.close(); - writeStream.close(); - resolve(); - }); - - writeStream.on("error", (err) => { - readStream.close(); - writeStream.close(); - reject(err); - }); - }); -} - -function flhash(str: string): number { - let hashvar1 = 0, - hashvar2 = 0; - - for (let i = 0; i < str.length; i++) { - hashvar1 = hashvar2 + str.charCodeAt(i); - hashvar2 = ((1025 * hashvar1) >> 6) ^ (1025 * hashvar1); - } - - const hash = 32769 * (((9 * hashvar2) >> 11) ^ (9 * hashvar2)); - return Number(`0x${hash.toString(16).slice(-8)}`); -} - export class PluginManager { - private static readonly defaultInitTimeoutMs = Number( - process.env.PLUGIN_INIT_TIMEOUT_MS ?? 10000 - ); - - private static resolveModulePath(basePath: string): string | null { - const candidates = [ - basePath, - `${basePath}.json`, - `${basePath}.js`, - `${basePath}.cjs`, - `${basePath}.mjs`, - path.join(basePath, "index.json"), - path.join(basePath, "index.js"), - path.join(basePath, "index.cjs"), - path.join(basePath, "index.mjs") - ]; - - for (const candidate of candidates) { - if (!fs.existsSync(candidate)) { - continue; - } - - try { - if (fs.statSync(candidate).isFile()) { - return candidate; - } - } catch { - // Skip inaccessible files and keep trying candidates. - } - } - - return null; - } - - public static getPluginDataRoots(): string[] { - if (process.env.DISABLE_PLUGINS) { - return []; - } - - const pluginsDir = - process.env.PLUGINS_DIR || path.join(process.cwd(), "plugins"); - - if (!folderExists(pluginsDir)) { - return []; - } - - return fs - .readdirSync(pluginsDir) - .sort((a, b) => a.localeCompare(b)) - .map((folder) => path.join(pluginsDir, folder, "data")) - .filter((pluginDataPath) => folderExists(pluginDataPath)); - } - - public static resolveServerDataModulePath(relativeDataPath: string): string { - const normalizedPath = relativeDataPath - .replaceAll("\\", "/") - .replace(/^\/+/, "") - .replace(/\/$/, ""); - - const pluginDataRoots = this.getPluginDataRoots(); - let pluginOverridePath: string | null = null; - - for (const pluginDataRoot of pluginDataRoots) { - const resolvedPluginPath = this.resolveModulePath( - path.join(pluginDataRoot, normalizedPath) - ); - if (resolvedPluginPath) { - pluginOverridePath = resolvedPluginPath; - } - } - - if (pluginOverridePath) { - return pluginOverridePath; - } - - const defaultPath = this.resolveModulePath( - path.join(process.cwd(), "data", normalizedPath) - ); - - if (defaultPath) { - return defaultPath; - } - - const packageDataPath = this.resolveModulePath( - path.join(__dirname, "../../../..", "data", normalizedPath) - ); - - if (packageDataPath) { - return packageDataPath; - } - - throw new Error( - `[PluginManager] Unable to resolve server data module for path: ${relativeDataPath}` - ); - } - - public static loadServerData(relativeDataPath: string): T { - return require(this.resolveServerDataModulePath(relativeDataPath)) as T; - } - private plugins: Array = []; get pluginCount() { return this.plugins.length; } - - private loadYaml(path: string) { - return yaml.load(fs.readFileSync(path, "utf8")) as any; - } private pluginsDir = process.env.PLUGINS_DIR || path.join(process.cwd(), "plugins"); private moduleDir = @@ -352,68 +195,22 @@ export class PluginManager { console.log("[PluginManager] Installing dependencies..."); - const installProcess = spawn("npm", ["install"], { - cwd: path.join(this.pluginsDir, pluginPath), - stdio: "inherit", - shell: process.platform === "win32" - }); + const installCommand = `npm install`; - installProcess.once("error", (error) => { + try { + execSync(installCommand, { + cwd: path.join(this.pluginsDir, pluginPath), + stdio: "inherit" + }); + console.log("[PluginManager] Dependencies installed successfully."); + resolve(); + } catch (error) { console.error(error); console.error("[PluginManager] Failed to install dependencies."); - reject(error); - }); - - installProcess.once("close", (code) => { - if (code === 0) { - console.log("[PluginManager] Dependencies installed successfully."); - resolve(); - return; - } - - const error = new Error( - `[PluginManager] npm install failed with exit code ${code ?? "unknown"}.` - ); - console.error(error); - reject(error); - }); - }); - } - - private async initializePluginWithTimeout( - plugin: BasePlugin, - server: ZoneServer2016 | LoginServer - ): Promise { - const timeoutMs = PluginManager.defaultInitTimeoutMs; - - try { - await this.loadPluginConfig(plugin); - console.time(`Loading ${plugin.name} plugin`); - await Promise.race([ - plugin.init(server), - new Promise((_, reject) => { - setTimeout(() => { - reject( - new Error( - `[PluginManager] ${plugin.name} init timeout after ${timeoutMs}ms.` - ) - ); - }, timeoutMs); - }) - ]); - console.timeEnd(`Loading ${plugin.name} plugin`); - - if (isZoneServer(server)) { - this.registerPluginCommands(server, plugin); - } else if (plugin.commands?.length) { - console.log( - `[PluginManager] ${plugin.name} tried adding commands, but LoginServer does not support plugin commands.` - ); + reject(); + process.exit(1); } - console.log(`[PluginManager] ${plugin.name} initialized!`); - } catch (e: any) { - console.error(e); - } + }); } /** @@ -473,15 +270,19 @@ export class PluginManager { /** * Loads the configuration for a plugin. + * @param server - The ZoneServer2016 instance. * @param plugin - The plugin instance. */ - private async loadPluginConfig(plugin: BasePlugin) { + private async loadPluginConfig(server: ZoneServer2016, plugin: BasePlugin) { const defaultConfigPath = path.join( plugin.dir, "data", "defaultconfig.yaml" ), - defaultConfig = this.loadYaml(defaultConfigPath), + defaultConfig = server.configManager.loadYaml( + defaultConfigPath, + false + ) as any, fileName = `${plugin.name .toLowerCase() .replaceAll(" ", "-")}-config.yaml`, @@ -494,7 +295,7 @@ export class PluginManager { await copyFile(defaultConfigPath, configPath); } - const configFile = this.loadYaml(configPath); + const configFile = server.configManager.loadYaml(configPath, false) as any; const config = { // in case a config file is outdated, load missing values using default @@ -521,7 +322,7 @@ export class PluginManager { * Initializes the plugins and loads their configurations. * @param server - The ZoneServer2016 instance. */ - public async initializePlugins(server: ZoneServer2016 | LoginServer) { + public async initializePlugins(server: ZoneServer2016) { // Used in tests if (process.env.DISABLE_PLUGINS) { return; @@ -536,10 +337,16 @@ export class PluginManager { await this.loadPlugins(); for (const plugin of this.plugins) { - // Keep startup responsive by not serially awaiting plugin init. - setImmediate(() => { - void this.initializePluginWithTimeout(plugin, server); - }); + try { + await this.loadPluginConfig(server, plugin); + console.time(`Loading ${plugin.name} plugin`); + await plugin.init(server); + console.timeEnd(`Loading ${plugin.name} plugin`); + this.registerPluginCommands(server, plugin); + console.log(`[PluginManager] ${plugin.name} initialized!`); + } catch (e: any) { + console.error(e); + } } if (this.plugins.length == 0) { diff --git a/src/servers/ZoneServer2016/managers/rewardmanager.ts b/src/servers/ZoneServer2016/managers/rewardmanager.ts index b68b265fdb..e49973eefb 100644 --- a/src/servers/ZoneServer2016/managers/rewardmanager.ts +++ b/src/servers/ZoneServer2016/managers/rewardmanager.ts @@ -15,7 +15,7 @@ import { setInterval } from "timers"; import { ZoneServer2016 } from "../zoneserver"; import { AccountItems } from "../models/enums"; import { ZoneClient2016 } from "../classes/zoneclient"; -import { isHalloween } from "../../../utils/utils"; +import { isHalloween, isPosInPoi } from "../../../utils/utils"; interface Reward { itemId: AccountItems; @@ -194,7 +194,7 @@ export class RewardManager { if ( client.character.playTime - client.character.lastDropPlaytime > (isHalloween() ? 60 : 120) && - this.server.isPosInPoi(client.character.state.position) + isPosInPoi(client.character.state.position) ) { this.dropPlayTimeReward(client); } diff --git a/src/servers/ZoneServer2016/managers/speedtreemanager.ts b/src/servers/ZoneServer2016/managers/speedtreemanager.ts index c3ba426f64..730f7e3f5b 100644 --- a/src/servers/ZoneServer2016/managers/speedtreemanager.ts +++ b/src/servers/ZoneServer2016/managers/speedtreemanager.ts @@ -12,12 +12,11 @@ // ====================================================================== import { PropInstance, SpeedTree, ZoneSpeedTreeData } from "types/zoneserver"; -import { randomIntFromInterval } from "../../../utils/utils"; +import { loadJson, randomIntFromInterval } from "../../../utils/utils"; import { ZoneClient2016 as Client } from "../classes/zoneclient"; import { Items, TreeIds } from "../models/enums"; import { ZoneServer2016 } from "../zoneserver"; import { ChallengeType } from "./challengemanager"; -import { PluginManager } from "./pluginmanager"; export class SpeedTreeManager { /** HashMap of destroyed trees, @@ -46,8 +45,8 @@ export class SpeedTreeManager { maxTreeHits!: number; initiateList() { - const Z1_speedTrees = PluginManager.loadServerData( - "2016/zoneData/Z1_speedTrees.json" + const Z1_speedTrees = loadJson( + __dirname + "/../../../../data/2016/zoneData/Z1_speedTrees.json" ); Z1_speedTrees.forEach((tree: any) => { this._speedTreesList.set(tree.uniqueId, { diff --git a/src/servers/ZoneServer2016/managers/weathermanager.ts b/src/servers/ZoneServer2016/managers/weathermanager.ts index 62c4bed3a9..7e0bce902e 100644 --- a/src/servers/ZoneServer2016/managers/weathermanager.ts +++ b/src/servers/ZoneServer2016/managers/weathermanager.ts @@ -18,12 +18,9 @@ import { _, isChristmasSeason } from "../../../utils/utils"; import { ZoneClient2016 as Client } from "../classes/zoneclient"; import { ZoneServer2016 } from "../zoneserver"; import EventEmitter from "node:events"; -import { PluginManager } from "./pluginmanager"; //const debug = require("debug")("dynamicWeather"); -const localWeatherTemplates = PluginManager.loadServerData( - "2016/dataSources/weather.json" -); +const localWeatherTemplates = require("../../../../data/2016/dataSources/weather.json"); function rnd_number( max: number, @@ -195,16 +192,14 @@ export class WeatherManager extends EventEmitter { }; if (server._soloMode) { this.templates[template.templateName] = template; - await fs.promises.writeFile( + fs.writeFileSync( `${__dirname}/../../../../data/2016/dataSources/weather.json`, JSON.stringify(this.templates, null, "\t") ); delete require.cache[ require.resolve("../../../../data/2016/dataSources/weather.json") ]; - this.templates = PluginManager.loadServerData( - "2016/dataSources/weather.json" - ); + this.templates = require("../../../../data/2016/dataSources/weather.json"); } else { await server._db?.collection("weathers").insertOne(template); this.templates = await (server._db as any) diff --git a/src/servers/ZoneServer2016/managers/worlddatamanager.ts b/src/servers/ZoneServer2016/managers/worlddatamanager.ts index eb2283a74c..13e67621ee 100644 --- a/src/servers/ZoneServer2016/managers/worlddatamanager.ts +++ b/src/servers/ZoneServer2016/managers/worlddatamanager.ts @@ -208,16 +208,13 @@ export class WorldDataManager { } async fetchWorldData(): Promise { - const [vehicles, constructionParents, freeplace, crops, traps] = - await Promise.all([ - this.loadVehiclesData() as Promise, - this.loadConstructionData() as Promise, - this.loadWorldFreeplaceConstruction() as Promise< - LootableConstructionSaveData[] - >, - this.loadCropData() as Promise, - this.loadTrapData() as Promise - ]); + const vehicles = (await this.loadVehiclesData()) as FullVehicleSaveData[]; + const constructionParents = + (await this.loadConstructionData()) as ConstructionParentSaveData[]; + const freeplace = + (await this.loadWorldFreeplaceConstruction()) as LootableConstructionSaveData[]; + const crops = (await this.loadCropData()) as PlantingDiameterSaveData[]; + const traps = (await this.loadTrapData()) as TrapSaveData[]; debug("World fetched!"); return { constructionParents, @@ -230,7 +227,7 @@ export class WorldDataManager { } async deleteServerData() { if (this._soloMode) { - await fs.promises.writeFile( + fs.writeFileSync( `${this._appDataFolder}/worlddata/world.json`, JSON.stringify({}, null, 2) ); @@ -243,7 +240,7 @@ export class WorldDataManager { async deleteCharacters() { if (this._soloMode) { - await fs.promises.writeFile( + fs.writeFileSync( `${this._appDataFolder}/single_player_characters2016.json`, JSON.stringify([], null, 2) ); @@ -258,28 +255,27 @@ export class WorldDataManager { } async deleteWorld() { - await Promise.all([this.deleteServerData(), this.deleteCharacters()]); + await this.deleteServerData(); + await this.deleteCharacters(); debug("World deleted!"); } async saveWorld(world: WorldArg) { console.time("WDM: saveWorld"); - await Promise.all([ - this.saveVehicles( - world.vehicles.filter( - (vehicle) => - ![VehicleIds.SPECTATE, VehicleIds.PARACHUTE].includes( - vehicle.vehicleId - ) - ) - ), - this.saveServerData(world.lastGuidItem), - this.saveCharacters(world.characters), - this.saveConstructionData(world.constructions), - this.saveWorldFreeplaceConstruction(world.worldConstructions), - this.saveCropData(world.crops), - this.saveTrapData(world.traps) - ]); + await this.saveVehicles( + world.vehicles.filter( + (vehicle) => + ![VehicleIds.SPECTATE, VehicleIds.PARACHUTE].includes( + vehicle.vehicleId + ) + ) + ); + await this.saveServerData(world.lastGuidItem); + await this.saveCharacters(world.characters); + await this.saveConstructionData(world.constructions); + await this.saveWorldFreeplaceConstruction(world.worldConstructions); + await this.saveCropData(world.crops); + await this.saveTrapData(world.traps); console.timeEnd("WDM: saveWorld"); } @@ -419,7 +415,7 @@ export class WorldDataManager { worldSaveVersion: this.worldSaveVersion }; if (this._soloMode) { - await fs.promises.writeFile( + fs.writeFileSync( `${this._appDataFolder}/worlddata/world.json`, JSON.stringify(saveData, null, 2) ); @@ -579,7 +575,7 @@ export class WorldDataManager { ...singlePlayerCharacter, ...characterSaveData }; - await fs.promises.writeFile( + fs.writeFileSync( `${this._appDataFolder}/single_player_characters2016.json`, JSON.stringify([singlePlayerCharacter], null, 2) ); @@ -767,30 +763,15 @@ export class WorldDataManager { } else { wall = this.loadConstructionDoorEntity(server, wallData); } - if (!parent.setWallSlot(server, wall)) { - console.error( - `[WDM] Wall slot registration failed for ${wall.characterId} (item ${wall.itemDefinitionId}, slot "${wall.slot}") on parent ${parent.characterId} — falling back to freeplace` - ); - parent.addFreeplaceConstruction(wall); - } + parent.setWallSlot(server, wall); }); Object.values(entityData.occupiedUpperWallSlots).forEach((wallData) => { const wall = this.loadConstructionChildEntity(server, wallData); - if (!parent.setWallSlot(server, wall)) { - console.error( - `[WDM] Upper wall slot registration failed for ${wall.characterId} (item ${wall.itemDefinitionId}, slot "${wall.slot}") on parent ${parent.characterId} — falling back to freeplace` - ); - parent.addFreeplaceConstruction(wall); - } + parent.setWallSlot(server, wall); }); Object.values(entityData.occupiedShelterSlots).forEach((shelterData) => { const shelter = this.loadConstructionChildEntity(server, shelterData); - if (!parent.setShelterSlot(server, shelter)) { - console.error( - `[WDM] Shelter slot registration failed for ${shelter.characterId} (item ${shelter.itemDefinitionId}, slot "${shelter.slot}") on parent ${parent.characterId} — falling back to freeplace` - ); - parent.addFreeplaceConstruction(shelter); - } + parent.setShelterSlot(server, shelter); }); Object.values(entityData.freeplaceEntities).forEach((freeplaceData) => { let freeplace: @@ -866,18 +847,7 @@ export class WorldDataManager { server, expansionData ); - if (!foundation.setExpansionSlot(expansion)) { - const slotNum = expansion.getSlotNumber(); - console.error( - `[WDM] Failed to register expansion slot "${expansion.slot}" (${expansion.characterId}) onto foundation ${foundation.characterId} — slot data may be corrupted` - ); - if (slotNum > 0) { - foundation.occupiedExpansionSlots[slotNum] = expansion; - console.error( - `[WDM] Force-inserted expansion at slot ${slotNum} to prevent data loss` - ); - } - } + foundation.setExpansionSlot(expansion); } ); @@ -934,14 +904,7 @@ export class WorldDataManager { server: ZoneServer2016 ) { constructionParents.forEach((parent) => { - try { - WorldDataManager.loadConstructionParentEntity(server, parent); - } catch (e) { - console.error( - `[WDM] Failed to load construction parent entity ${parent.characterId} (item ${parent.itemDefinitionId}) — skipping to avoid blocking remaining entities`, - e - ); - } + WorldDataManager.loadConstructionParentEntity(server, parent); }); } @@ -1075,7 +1038,7 @@ export class WorldDataManager { if (!constructions.length) return; if (this._soloMode) { - await fs.promises.writeFile( + fs.writeFileSync( `${this._appDataFolder}/worlddata/construction.json`, JSON.stringify(constructions, null, 2) ); @@ -1139,7 +1102,7 @@ export class WorldDataManager { async saveCropData(crops: PlantingDiameterSaveData[]) { if (this._soloMode) { - await fs.promises.writeFile( + fs.writeFileSync( `${this._appDataFolder}/worlddata/crops.json`, JSON.stringify(crops, null, 2) ); @@ -1269,7 +1232,7 @@ export class WorldDataManager { async saveVehicles(vehicles: FullVehicleSaveData[]) { if (this._soloMode) { - await fs.promises.writeFile( + fs.writeFileSync( `${this._appDataFolder}/worlddata/vehicles.json`, JSON.stringify(vehicles, null, 2) ); @@ -1301,7 +1264,7 @@ export class WorldDataManager { freeplaces: LootableConstructionSaveData[] ) { if (this._soloMode) { - await fs.promises.writeFile( + fs.writeFileSync( `${this._appDataFolder}/worlddata/worldconstruction.json`, JSON.stringify(freeplaces, null, 2) ); @@ -1367,7 +1330,7 @@ export class WorldDataManager { async saveTrapData(traps: TrapSaveData[]) { if (this._soloMode) { - await fs.promises.writeFile( + fs.writeFileSync( `${this._appDataFolder}/worlddata/traps.json`, JSON.stringify(traps, null, 2) ); diff --git a/src/servers/ZoneServer2016/managers/worldobjectmanager.test.ts b/src/servers/ZoneServer2016/managers/worldobjectmanager.test.ts index 32b0f20130..95421470f9 100644 --- a/src/servers/ZoneServer2016/managers/worldobjectmanager.test.ts +++ b/src/servers/ZoneServer2016/managers/worldobjectmanager.test.ts @@ -12,21 +12,17 @@ // ====================================================================== import test, { after } from "node:test"; +import { containerLootSpawners } from "../data/lootspawns"; import assert from "node:assert"; -import { LootTableManager } from "./loottablemanager"; test("WorldObjectManager", { timeout: 10000 }, async (t) => { await t.test("containerLootSpawners", () => { - const manager = new LootTableManager(); - manager.load(); - const containerTables = manager.getContainerTables(); - for (const key in containerTables) { - const containerLootTable = containerTables[key]; - for (const pool of containerLootTable.pools) { - if (!pool.rolls) continue; + for (const key in containerLootSpawners) { + const containerLootTable = containerLootSpawners[key]; + if (containerLootTable.maxItems) { assert( - pool.rolls.max <= pool.entries.length, - `${key} pool rolls.max (${pool.rolls.max}) exceeds entry count (${pool.entries.length})` + containerLootTable.maxItems <= containerLootTable.items.length, + `${key} MaxItems is > items.length` ); } } diff --git a/src/servers/ZoneServer2016/managers/worldobjectmanager.ts b/src/servers/ZoneServer2016/managers/worldobjectmanager.ts index cf74f5bd62..b3713e2f84 100644 --- a/src/servers/ZoneServer2016/managers/worldobjectmanager.ts +++ b/src/servers/ZoneServer2016/managers/worldobjectmanager.ts @@ -12,25 +12,16 @@ // ====================================================================== import { ZoneServer2016 } from "../zoneserver"; -import { PluginManager } from "./pluginmanager"; -const Z1_doors = PluginManager.loadServerData("2016/zoneData/Z1_doors.json"); -const Z1_items = PluginManager.loadServerData("2016/zoneData/Z1_items.json"); -const Z1_vehicles = PluginManager.loadServerData( - "2016/zoneData/Z1_vehicleLocations.json" -); -const Z1_npcs = PluginManager.loadServerData("2016/zoneData/Z1_npcs.json"); -const Z1_lootableProps = PluginManager.loadServerData( - "2016/zoneData/Z1_lootableProps.json" -); -const Z1_taskProps = PluginManager.loadServerData( - "2016/zoneData/Z1_taskProps.json" -); -const Z1_crates = PluginManager.loadServerData("2016/zoneData/Z1_crates.json"); -const Z1_destroyables = PluginManager.loadServerData( - "2016/zoneData/Z1_destroyables.json" -); -const models = PluginManager.loadServerData("2016/dataSources/Models.json"); -// const bannedZombieModels = PluginManager.loadServerData("2016/sampleData/bannedZombiesModels.json"); +const Z1_doors = require("../../../../data/2016/zoneData/Z1_doors.json"); +const Z1_items = require("../../../../data/2016/zoneData/Z1_items.json"); +const Z1_vehicles = require("../../../../data/2016/zoneData/Z1_vehicleLocations.json"); +const Z1_npcs = require("../../../../data/2016/zoneData/Z1_npcs.json"); +const Z1_lootableProps = require("../../../../data/2016/zoneData/Z1_lootableProps.json"); +const Z1_taskProps = require("../../../../data/2016/zoneData/Z1_taskProps.json"); +const Z1_crates = require("../../../../data/2016/zoneData/Z1_crates.json"); +const Z1_destroyables = require("../../../../data/2016/zoneData/Z1_destroyables.json"); +const models = require("../../../../data/2016/dataSources/Models.json"); +// const bannedZombieModels = require("../../../../data/2016/sampleData/bannedZombiesModels.json"); import { _, eul2quat, @@ -38,7 +29,8 @@ import { isPosInRadius, randomIntFromInterval, fixEulerOrder, - getCurrentServerTimeWrapper + getCurrentServerTimeWrapper, + isLootNerfedLoc } from "../../../utils/utils"; import { EquipSlots, @@ -54,11 +46,12 @@ import { VehicleIds } from "../models/enums"; import { Vehicle2016 } from "../entities/vehicle"; +import { LootDefinition } from "types/zoneserver"; import { ItemObject } from "../entities/itemobject"; import { DoorEntity } from "../entities/doorentity"; import { BaseFullCharacter } from "../entities/basefullcharacter"; import { ExplosiveEntity } from "../entities/explosiveentity"; -import { LootTableManager } from "./loottablemanager"; +import { lootTables, containerLootSpawners } from "../data/lootspawns"; import { BaseItem } from "../classes/baseItem"; import { Lootbag } from "../entities/lootbag"; import { LootableProp } from "../entities/lootableprop"; @@ -72,17 +65,7 @@ import { TreasureChest } from "../entities/treasurechest"; import { Npc } from "../entities/npc"; //import { EntityType } from "h1emu-ai"; import { scheduler } from "node:timers/promises"; -import { - ContainerPropSnapshot, - ItemDespawnSnapshot, - LootSpawnWorker, - LootbagDespawnSnapshot, - NpcDespawnSnapshot, - SpawnedItemSnapshot -} from "./lootspawnworker"; -import type { ItemFunction } from "types/zoneserver"; const debug = require("debug")("ZoneServer"); -const apm = require("elastic-apm-node"); export function getRandomSkin(itemDefinitionId: number) { let itemDefId = 0; @@ -113,9 +96,7 @@ export function getRandomSkin(itemDefinitionId: number) { return itemDefId; } -export function getRandomItem( - items: Array -): T | undefined { +export function getRandomItem(items: Array) { const totalWeight = items.reduce((total, item) => total + item.weight, 0), randomWeight = Math.random() * totalWeight; let currentWeight = 0; @@ -126,24 +107,6 @@ export function getRandomItem( return items[i]; } } - return undefined; -} - -function applyItemFunctions(item: BaseItem, functions: ItemFunction[]): void { - for (const fn of functions) { - if (fn.function === "set_damage") { - const fraction = fn.min + Math.random() * (fn.max - fn.min); - item.currentDurability = Math.max( - 1, - Math.floor(item.currentDurability * fraction) - ); - } else if (fn.function === "set_count") { - item.stackCount = Math.max( - 1, - Math.floor(Math.random() * (fn.max - fn.min + 1) + fn.min) - ); - } - } } export class WorldObjectManager { @@ -176,8 +139,6 @@ export class WorldObjectManager { chanceWornLetter!: number; waterSourceReplenishTimer!: number; waterSourceRefillAmount!: number; - gridScrapLimit!: number; - gridScrapLimitEnabled!: boolean; private zombieSlots = [ EquipSlots.HEAD, @@ -188,12 +149,6 @@ export class WorldObjectManager { EquipSlots.HAIR ]; static itemSpawnersChances: Record = {}; - private isRunning = false; - private lootSpawnWorker?: LootSpawnWorker; - readonly lootTableManager = new LootTableManager(); - maxNpcDespawnsPerRun = 40; - maxLootbagDespawnsPerRun = 40; - maxItemDespawnsPerRun = 120; private getItemRespawnTimer(server: ZoneServer2016): void { if (this.hasCustomLootRespawnTime) return; @@ -214,333 +169,114 @@ export class WorldObjectManager { } async run(server: ZoneServer2016) { - if (this.isRunning) return; - this.isRunning = true; - const transaction = apm.startTransaction( - "WorldObjectManager::Run", - "custom" - ); debug("WOM::Run"); - try { - if (server.isSurvival()) { - this.getItemRespawnTimer(server); - if (this._lastLootRespawnTime + this.lootRespawnTimer <= Date.now()) { - if (this.gridScrapLimitEnabled) { - this.refillScrapInChunks(server); - } - await this.createLootThreaded(server); - await this.createContainerLootThreaded(server); - this._lastLootRespawnTime = Date.now(); - server.divideLargeCells(700); - } - if (this._lastNpcRespawnTime + this.npcRespawnTimer <= Date.now()) { - await this.createNpcsThreaded(server); - this._lastNpcRespawnTime = Date.now(); - } - if ( - this._lastVehicleRespawnTime + this.vehicleRespawnTimer <= - Date.now() - ) { - this.createVehicles(server); - this._lastVehicleRespawnTime = Date.now(); - } - if ( - this._lastWaterSourceReplenishTime + this.waterSourceReplenishTimer <= - Date.now() - ) { - this.replenishWaterSources(server); - this._lastWaterSourceReplenishTime = Date.now(); - } - - await this.updateQuestContainers(server); + if (server.isSurvival()) { + this.getItemRespawnTimer(server); + if (this._lastLootRespawnTime + this.lootRespawnTimer <= Date.now()) { + this.refillScrapInChunks(server); + this.createLoot(server); + this.createContainerLoot(server); + this._lastLootRespawnTime = Date.now(); + server.divideLargeCells(700); } - - if (server.isSurvival()) { - await this.despawnEntities(server); + if (this._lastNpcRespawnTime + this.npcRespawnTimer <= Date.now()) { + this.createNpcs(server); + this._lastNpcRespawnTime = Date.now(); } - } finally { - transaction.end(); - this.isRunning = false; - } - } - - private getLootSpawnWorker(): LootSpawnWorker { - if (!this.lootSpawnWorker) { - this.lootSpawnWorker = new LootSpawnWorker({ - groundTables: this.lootTableManager.getGroundTables(), - containerTables: this.lootTableManager.getContainerTables() - }); - } - return this.lootSpawnWorker; - } - - async stop() { - if (!this.lootSpawnWorker) return; - await this.lootSpawnWorker.stop(); - this.lootSpawnWorker = undefined; - } - - async createLootThreaded(server: ZoneServer2016) { - try { - const worker = this.getLootSpawnWorker(); - const spawnedLootSpawnerIds: number[] = []; - for (const spawnerId in this.spawnedLootObjects) { - spawnedLootSpawnerIds.push(Number(spawnerId)); + if ( + this._lastVehicleRespawnTime + this.vehicleRespawnTimer <= + Date.now() + ) { + this.createVehicles(server); + this._lastVehicleRespawnTime = Date.now(); } - const spawnedItemSnapshots: SpawnedItemSnapshot[] = []; - for (const characterId in server._spawnedItems) { - const itemObject = server._spawnedItems[characterId]; - spawnedItemSnapshots.push({ - position: [ - itemObject.state.position[0], - itemObject.state.position[1], - itemObject.state.position[2] - ] as [number, number, number], - itemDefinitionId: itemObject.item.itemDefinitionId - }); + if ( + this._lastWaterSourceReplenishTime + this.waterSourceReplenishTimer <= + Date.now() + ) { + this.replenishWaterSources(server); + this._lastWaterSourceReplenishTime = Date.now(); } - const ingameHour = (server.inGameTimeManager.time / 3600) % 24; - const plan = await worker.createLootPlan( - spawnedLootSpawnerIds, - spawnedItemSnapshots, - ingameHour - ); - for (const entry of plan) { - if (this.spawnedLootObjects[entry.spawnerId]) continue; - const item = server.generateItem( - getRandomSkin(entry.itemDefinitionId), - entry.count - ); - if (item && entry.functions?.length) - applyItemFunctions(item, entry.functions); - this.createLootEntity( - server, - item, - new Float32Array(entry.position), - new Float32Array(entry.rotation), - entry.spawnerId - ); - } - } catch (error) { - debug(`[WARN] createLootThreaded fallback to main thread: ${error}`); - await this.createLoot(server); + this.updateQuestContainers(server); } - } - async createContainerLootThreaded(server: ZoneServer2016) { - try { - const worker = this.getLootSpawnWorker(); - const props: ContainerPropSnapshot[] = []; - - for (const characterId in server._lootableProps) { - const prop = server._lootableProps[characterId] as LootableProp; - if (!prop.shouldSpawnLoot) continue; - const container = prop.getContainer(); - if (!container) continue; - if (Object.keys(container.items).length > 0) continue; + if (server.isSurvival()) { + this.despawnEntities(server); + } + } - props.push({ - characterId, - lootSpawner: prop.lootSpawner, - shouldSpawnLoot: true, - position: [ - prop.state.position[0], - prop.state.position[1], - prop.state.position[2] - ], - existingItemDefinitionIds: [] - }); + private async npcDespawner(server: ZoneServer2016) { + let counter = 0; + for (const characterId in server._npcs) { + if (counter > 30) { + counter = 0; + await scheduler.wait(30); } - - const plan = await worker.createContainerLootPlan(props); - const updatedProps = new Set(); - - for (const entry of plan) { - const prop = server._lootableProps[entry.characterId] as LootableProp; - if (!prop) continue; - const container = prop.getContainer(); - if (!container) continue; - - const hasSameItem = Object.values(container.items).some( - (spawnedItem: BaseItem) => - spawnedItem.itemDefinitionId === entry.itemDefinitionId - ); - if (hasSameItem) continue; - - const item = server.generateItem( - getRandomSkin(entry.itemDefinitionId), - entry.count - ); - if (item && entry.functions?.length) - applyItemFunctions(item, entry.functions); - server.addContainerItem(prop, item, container); - updatedProps.add(entry.characterId); + counter++; + const npc = server._npcs[characterId]; + // dead npc despawner + if ( + npc && + npc.flags.knockedOut && + Date.now() - npc.deathTime >= this.deadNpcDespawnTimer + ) { + server.deleteEntity(npc.characterId, server._npcs); } - - if (!updatedProps.size) return; - Object.values(server._clients).forEach((client: ZoneClient2016) => { - updatedProps.forEach((characterId) => { - const prop = server._lootableProps[characterId] as LootableProp; - const index = client.searchedProps.indexOf(prop); - if (index > -1) { - client.searchedProps.splice(index, 1); - } - }); - }); - } catch (error) { - debug( - `[WARN] createContainerLootThreaded fallback to main thread: ${error}` - ); - await this.createContainerLoot(server); } } - async createNpcsThreaded(server: ZoneServer2016) { - try { - const worker = this.getLootSpawnWorker(); - const existingNpcPositions = Object.values(server._npcs).map((npc) => [ - npc.state.position[0], - npc.state.position[1], - npc.state.position[2] - ]); - - const plan = await worker.createNpcPlan( - existingNpcPositions, - this.npcSpawnRadius, - this.chanceNpc, - this.chanceScreamer - ); - - for (const entry of plan) { - this.createNpc( - server, - entry.modelId, - new Float32Array(entry.position), - new Float32Array(eul2quat(new Float32Array(entry.rotation))), - entry.spawnerId - ); + private lootbagDespawner(server: ZoneServer2016) { + for (const characterId in server._lootbags) { + // lootbag despawner + const lootbag = server._lootbags[characterId]; + if (Date.now() - lootbag.creationTime >= this.lootbagDespawnTimer) { + server.deleteEntity(lootbag.characterId, server._lootbags); } - } catch (error) { - debug(`[WARN] createNpcsThreaded fallback to main thread: ${error}`); - await this.createNpcs(server); } } - private async despawnEntities(server: ZoneServer2016) { - try { - const worker = this.getLootSpawnWorker(); - const now = Date.now(); - - const npcs: NpcDespawnSnapshot[] = Object.values(server._npcs).map( - (npc) => ({ - characterId: npc.characterId, - knockedOut: !!npc.flags.knockedOut, - deathTime: npc.deathTime - }) - ); - const lootbags: LootbagDespawnSnapshot[] = Object.values( - server._lootbags - ).map((lootbag) => ({ - characterId: lootbag.characterId, - creationTime: lootbag.creationTime - })); - const items: ItemDespawnSnapshot[] = Object.values( - server._spawnedItems - ).map((itemObject) => ({ - characterId: itemObject.characterId, - creationTime: itemObject.creationTime, - spawnerId: itemObject.spawnerId, - itemDefinitionId: itemObject.item.itemDefinitionId - })); - - const plan = await worker.createDespawnPlan({ - now, - deadNpcDespawnTimer: this.deadNpcDespawnTimer, - lootbagDespawnTimer: this.lootbagDespawnTimer, - itemDespawnTimer: this.itemDespawnTimer, - lootDespawnTimer: this.lootDespawnTimer, - fuelItemDefinitionIds: [Items.FUEL_ETHANOL, Items.FUEL_BIOFUEL], - npcs, - lootbags, - items - }); - - for (const characterId of plan.npcCharacterIds.slice( - 0, - this.maxNpcDespawnsPerRun - )) { - server.deleteEntity(characterId, server._npcs); - } - - for (const characterId of plan.lootbagCharacterIds.slice( - 0, - this.maxLootbagDespawnsPerRun - )) { - server.deleteEntity(characterId, server._lootbags); + private async itemDespawner(server: ZoneServer2016) { + let counter = 0; + for (const characterId in server._spawnedItems) { + if (counter > 100) { + counter = 0; + await scheduler.wait(30); } - - for (const entry of plan.itemEntries.slice( - 0, - this.maxItemDespawnsPerRun - )) { - const itemObject = server._spawnedItems[entry.characterId]; - if (!itemObject) continue; + counter++; + const itemObject = server._spawnedItems[characterId]; + if (!itemObject) return; + // dropped item despawner + const despawnTime = + itemObject.spawnerId == -1 + ? this.itemDespawnTimer + : this.lootDespawnTimer; + if (Date.now() - itemObject.creationTime >= despawnTime) { server.deleteEntity(itemObject.characterId, server._spawnedItems); - if (entry.deleteExplosive) { - server.deleteEntity(itemObject.characterId, server._explosives); - } - if (entry.spawnerId != -1) { - delete this.spawnedLootObjects[entry.spawnerId]; + switch (itemObject.item.itemDefinitionId) { + case Items.FUEL_ETHANOL: + case Items.FUEL_BIOFUEL: + server.deleteEntity(itemObject.characterId, server._explosives); + break; } + if (itemObject.spawnerId != -1) + delete this.spawnedLootObjects[itemObject.spawnerId]; server.sendCompositeEffectToAllWithSpawnedEntity( server._spawnedItems, itemObject, - server.getItemDefinition(entry.itemDefinitionId)?.PICKUP_EFFECT ?? - 5151 + server.getItemDefinition(itemObject.item.itemDefinitionId) + ?.PICKUP_EFFECT ?? 5151 ); } - } catch (error) { - debug(`[WARN] despawnEntities threaded path failed: ${error}`); - // Safe fallback to avoid leaking stale entities if worker path fails. - for (const characterId in server._npcs) { - const npc = server._npcs[characterId]; - if ( - npc && - npc.flags.knockedOut && - Date.now() - npc.deathTime >= this.deadNpcDespawnTimer - ) { - server.deleteEntity(npc.characterId, server._npcs); - } - } - for (const characterId in server._lootbags) { - const lootbag = server._lootbags[characterId]; - if (Date.now() - lootbag.creationTime >= this.lootbagDespawnTimer) { - server.deleteEntity(lootbag.characterId, server._lootbags); - } - } - for (const characterId in server._spawnedItems) { - const itemObject = server._spawnedItems[characterId]; - if (!itemObject) continue; - const despawnTime = - itemObject.spawnerId == -1 - ? this.itemDespawnTimer - : this.lootDespawnTimer; - if (Date.now() - itemObject.creationTime >= despawnTime) { - server.deleteEntity(itemObject.characterId, server._spawnedItems); - if ( - itemObject.item.itemDefinitionId == Items.FUEL_ETHANOL || - itemObject.item.itemDefinitionId == Items.FUEL_BIOFUEL - ) { - server.deleteEntity(itemObject.characterId, server._explosives); - } - if (itemObject.spawnerId != -1) - delete this.spawnedLootObjects[itemObject.spawnerId]; - } - } } } + private despawnEntities(server: ZoneServer2016) { + this.npcDespawner(server); + this.lootbagDespawner(server); + this.itemDespawner(server); + } + createNpc( server: ZoneServer2016, modelId: number, @@ -684,14 +420,13 @@ export class WorldObjectManager { const index = Math.floor(Math.random() * airdropTypes.length); let airdropType = airdropTypes[index]; + let lootSpawner = containerLootSpawners[airdropType]; if (forceAirdrop.length > 0) { airdropType = forceAirdrop; + lootSpawner = containerLootSpawners[forceAirdrop]; } - const containerTables = this.lootTableManager.getContainerTables(); - const lootSpawner = containerTables[airdropType]; - const characterId = generateRandomGuid(); const lootbag = new Lootbag( @@ -703,15 +438,11 @@ export class WorldObjectManager { server ); const container = lootbag.getContainer(); - if (container && lootSpawner) { - // Airdrop: spawn every item entry at max count regardless of weights/conditions - const allEntries = lootSpawner.pools - .flatMap((p) => p.entries) - .filter((e) => (e.type ?? "item") === "item" && e.item !== undefined); - allEntries.forEach((entry) => { + if (container) { + lootSpawner.items.forEach((item: LootDefinition) => { server.addContainerItem( lootbag, - server.generateItem(entry.item!, entry.count?.max ?? 1), + server.generateItem(item.item, item.spawnCount.max), container ); }); @@ -1039,7 +770,7 @@ export class WorldObjectManager { for (const a in server._taskProps) { if (counter > 9) { counter = 0; - await scheduler.yield(); + await scheduler.wait(60); } counter++; const propInstance = server._taskProps[a]; @@ -1189,18 +920,9 @@ export class WorldObjectManager { ); server._vehicles[vehicle.characterId] = vehicle; - // Immediately send the vehicle to any clients in range - for (const sessionId in server._clients) { - const client = server._clients[sessionId]; - if (!client.isLoading) server.vehicleManager(client); - } } createVehicles(server: ZoneServer2016, maxSpawnChance: boolean = false) { - const transaction = apm.startTransaction( - "WorldObjectManager::createVehicles", - "custom" - ); if (_.size(server._vehicles) >= this.vehicleSpawnCap) return; const respawnAmount = Math.ceil( (this.vehicleSpawnCap - _.size(server._vehicles)) / 8 @@ -1238,11 +960,10 @@ export class WorldObjectManager { vehicleData.positionUpdate.orientation = dataVehicle.orientation; this.createVehicle(server, vehicleData, maxSpawnChance); // save vehicle } - transaction.end(); debug("All vehicles created"); } - private async createNpcs(server: ZoneServer2016) { + async createNpcs(server: ZoneServer2016) { // This is only for giving the world some life for (const spawnerType of Z1_npcs) { const authorizedModelId: number[] = []; @@ -1275,7 +996,7 @@ export class WorldObjectManager { for (const a in server._npcs) { if (counter > 150) { counter = 0; - await scheduler.yield(); + await scheduler.wait(30); } counter++; if (!server._npcs[a]) continue; @@ -1316,54 +1037,47 @@ export class WorldObjectManager { for (let x = 0; x < server._grid.length; x++) { const chunk = server._grid[x]; chunk.availableScrap += 20; - if (chunk.availableScrap > this.gridScrapLimit) - chunk.availableScrap = this.gridScrapLimit; + if (chunk.availableScrap > 50) chunk.availableScrap = 50; } } - private async createLoot(server: ZoneServer2016) { - const transaction = apm.startTransaction( - "WorldObjectManager::createLoot", - "custom" - ); - const groundTables = this.lootTableManager.getGroundTables(); + async createLoot(server: ZoneServer2016, lTables = lootTables) { let counter = 0; for (const spawnerType of Z1_items) { - const span = transaction.startSpan("spawnerType"); - const lootTable = groundTables[spawnerType.actorDefinition]; + const lootTable = lTables[spawnerType.actorDefinition]; if (lootTable) { - const allEntries = lootTable.pools.flatMap((p) => p.entries); for (const itemInstance of spawnerType.instances) { if (counter > 9) { counter = 0; - await scheduler.yield(); + await scheduler.wait(60); } counter++; if (this.spawnedLootObjects[itemInstance.id]) continue; const chance = Math.floor(Math.random() * 100) + 1; - if (chance <= lootTable.spawnChance) { + if ( + chance <= + lootTable.spawnChance * + (1 - isLootNerfedLoc(itemInstance.position) / 100) + ) { if (!WorldObjectManager.itemSpawnersChances[itemInstance.id]) { const realSpawnChance = - ((lootTable.spawnChance / allEntries.length) * + ((lootTable.spawnChance / lootTable.items.length) * spawnerType.instances.length) / 100; WorldObjectManager.itemSpawnersChances[ spawnerType.actorDefinition ] = realSpawnChance; } - const entry = getRandomItem(allEntries); - if ( - entry && - (entry.type ?? "item") === "item" && - entry.item !== undefined - ) { + const item = getRandomItem(lootTable.items); + if (item) { this.createLootEntity( server, server.generateItem( - getRandomSkin(entry.item), - entry.count - ? randomIntFromInterval(entry.count.min, entry.count.max) - : 1 + getRandomSkin(item.item), + randomIntFromInterval( + item.spawnCount.min, + item.spawnCount.max + ) ), new Float32Array(itemInstance.position), new Float32Array(itemInstance.rotation), @@ -1373,9 +1087,7 @@ export class WorldObjectManager { } } } - span?.end(); } - transaction.end(); } async updateQuestContainers(server: ZoneServer2016) { @@ -1383,7 +1095,7 @@ export class WorldObjectManager { for (const a in server._lootableProps) { if (counter > 100) { counter = 0; - await scheduler.yield(); + await scheduler.wait(30); // Await the wait function to pause } counter++; const prop = server._lootableProps[a] as BaseFullCharacter; @@ -1587,57 +1299,53 @@ export class WorldObjectManager { } } } - private async createContainerLoot(server: ZoneServer2016) { - const transaction = apm.startTransaction( - "WorldObjectManager::createContainerLoot", - "custom" - ); - const containerTables = this.lootTableManager.getContainerTables(); + async createContainerLoot(server: ZoneServer2016) { let counter = 0; for (const a in server._lootableProps) { if (counter > 9) { counter = 0; - await scheduler.yield(); + await scheduler.wait(60); // Await the wait function to pause } counter++; const prop = server._lootableProps[a] as LootableProp; const container = prop.getContainer(); if (!container) continue; - if (!!Object.keys(container.items).length) continue; - if (!prop.shouldSpawnLoot) continue; - const lootTable = containerTables[prop.lootSpawner]; + if (!!Object.keys(container.items).length) continue; // skip if container is not empty + if (!prop.shouldSpawnLoot) continue; // skip medical stations and treasure chests + const lootTable = containerLootSpawners[prop.lootSpawner]; if (lootTable) { - const containerItemIds = new Set( - Object.values(container.items).map( - (spawnedItem: BaseItem) => spawnedItem.itemDefinitionId - ) - ); - for (const pool of lootTable.pools) { - const rolls = pool.rolls - ? randomIntFromInterval(pool.rolls.min, pool.rolls.max) - : 1; - for (let r = 0; r < rolls; r++) { - const entry = getRandomItem(pool.entries); + for (let x = 0; x < lootTable.maxItems; x++) { + const item = getRandomItem(lootTable.items); + if (!item) continue; + const chance = Math.floor(Math.random() * 100) + 1; // temporary spawnchance + let allow = true; + Object.values(container.items).forEach((spawnedItem: BaseItem) => { + if (item.item == spawnedItem.itemDefinitionId) allow = false; // dont allow the same item to be added twice + }); + if (allow) { if ( - !entry || - (entry.type ?? "item") !== "item" || - entry.item === undefined - ) - continue; - if (containerItemIds.has(entry.item)) continue; - const count = entry.count - ? randomIntFromInterval(entry.count.min, entry.count.max) - : 1; - server.addContainerItem( - prop, - server.generateItem(getRandomSkin(entry.item), count), - container - ); - containerItemIds.add(entry.item); + chance <= + item.weight * (1 - isLootNerfedLoc(prop.state.position) / 100) + ) { + const count = Math.floor( + Math.random() * + (item.spawnCount.max - item.spawnCount.min + 1) + + item.spawnCount.min + ); + // temporary spawnchance + server.addContainerItem( + prop, + server.generateItem(getRandomSkin(item.item), count), + container + ); + } + } else { + x--; } } } if (Object.keys(container.items).length != 0) { + // mark prop as unsearched for clients Object.values(server._clients).forEach((client: ZoneClient2016) => { const index = client.searchedProps.indexOf(prop); if (index > -1) { @@ -1646,6 +1354,5 @@ export class WorldObjectManager { }); } } - transaction.end(); } } diff --git a/src/servers/ZoneServer2016/models/config.ts b/src/servers/ZoneServer2016/models/config.ts index 67d8d24782..c971070749 100644 --- a/src/servers/ZoneServer2016/models/config.ts +++ b/src/servers/ZoneServer2016/models/config.ts @@ -35,7 +35,6 @@ interface ServerConfig { damageWeapons: boolean; disablePOIManager: boolean; disableMapBoundsCheck: boolean; - disableBaseCheck: boolean; } interface RconConfig { @@ -79,9 +78,6 @@ interface WorldObjectsConfig { lootDespawnTimer: number; deadNpcDespawnTimer: number; lootbagDespawnTimer: number; - maxNpcDespawnsPerRun: number; - maxLootbagDespawnsPerRun: number; - maxItemDespawnsPerRun: number; chanceWornLetter: number; @@ -92,9 +88,6 @@ interface WorldObjectsConfig { crowbarHitRewardChance: number; crowbarHitDamage: number; - - gridScrapLimit: number; - gridScrapLimitEnabled: boolean; } interface SpeedTreeConfig { diff --git a/src/servers/ZoneServer2016/zonepackethandlers.ts b/src/servers/ZoneServer2016/zonepackethandlers.ts index 6c55045d03..24aaa92d0c 100644 --- a/src/servers/ZoneServer2016/zonepackethandlers.ts +++ b/src/servers/ZoneServer2016/zonepackethandlers.ts @@ -168,7 +168,6 @@ import { LoadoutItem } from "./classes/loadoutItem"; import { BaseItem } from "./classes/baseItem"; import { Collection } from "mongodb"; import { ItemObject } from "./entities/itemobject"; -import { ExplosiveEntity } from "./entities/explosiveentity"; function getStanceFlags(num: number): StanceFlags { function getBit(bin: string, bit: number) { @@ -351,24 +350,25 @@ export class ZonePacketHandlers { ); client.character.state.position = awaitingPos; client.character.awaitingTeleportLocation = undefined; - // Defer the de-spawn sweep so concurrent teleports don't stack. - setImmediate(() => { - // fixes characters showing up as dead if they respawn close to other characters - // also clear spawnedEntities so the server and client stay in sync - for (const a in server._clients) { - const c = server._clients[a]; - if (c === client) continue; - if (c.spawnedEntities.has(client.character)) { - server.sendData(c, "Character.RemovePlayer", { - characterId: client.character.characterId - }); - c.spawnedEntities.delete(client.character); - } + // fixes characters showing up as dead if they respawn close to other characters + server.sendDataToAllOthersWithSpawnedEntity( + server._characters, + client, + client.character.characterId, + "Character.RemovePlayer", + { + characterId: client.character.characterId } - }); + ); setTimeout(() => { if (!client?.character) return; - server.spawnCharacterToOtherClients(client.character); + server.sendDataToAllOthersWithSpawnedEntity( + server._characters, + client, + client.character.characterId, + "AddLightweightPc", + client.character.pGetLightweightPC(server, client) + ); }, 2000); }, 100); } @@ -531,6 +531,7 @@ export class ZonePacketHandlers { ); } server.spawnContainerAccessNpc(client); + server.setTickRate(); } Security( server: ZoneServer2016, @@ -854,12 +855,10 @@ export class ZonePacketHandlers { packet: ReceivedPacket ) { if (client.isLoading && client.characterReleased && client.isSynced) { - const isFirstReleased = client.firstReleased; - client.firstReleased = false; setTimeout(() => { client.isLoading = false; if (!client.characterReleased) return; - if (isFirstReleased) { + if (client.firstReleased) { server.sendData(client, "H1emu.VoiceInit", { args: `${server.voiceChatManager.serverAddress} ${server._worldId}` }); @@ -871,10 +870,11 @@ export class ZonePacketHandlers { server.fairPlayManager.handleAssetValidationInit(server, client); } if ( - isFirstReleased && + client.firstReleased && client.startingPos && client.character.state.position[1] < client.startingPos[1] ) { + client.firstReleased = false; server.sendData( client, "ClientUpdate.UpdateLocation", @@ -885,6 +885,7 @@ export class ZonePacketHandlers { ); client.character.state.position = client.startingPos; } + client.firstReleased = false; server.executeRoutine(client); }, 500); } @@ -1501,10 +1502,20 @@ export class ZonePacketHandlers { if (stance) { const stanceFlags = getStanceFlags(stance); + if (stanceFlags.SITTING) { + server.sendData(client, "Command.RunSpeed", { + runSpeed: 0.1 + }); + setTimeout(() => { + server.sendData(client, "Command.RunSpeed", { + runSpeed: 0 + }); + }, 2000); + } + // Detect movements based on stance server.fairPlayManager.detectJumpXSMovement(server, client, stanceFlags); server.fairPlayManager.detectDroneMovement(server, client, stanceFlags); - server.detectSnaking(server, client, stanceFlags); server.detectEnasMovement(server, client, stanceFlags); // Handle jump logic @@ -1613,20 +1624,6 @@ export class ZonePacketHandlers { } client.character.state.position = position; - // Check if player stepped on an armed landmine - if (server.aiManager.explosiveEntities.size > 0) { - const landmineCells = server.getGridCellsInRadius(position, 0.6); - for (const cell of landmineCells) { - for (const obj of cell.objects) { - if (!(obj instanceof ExplosiveEntity)) continue; - if (!obj.isArmed) continue; - if (isPosInRadiusWithY(0.6, position, obj.state.position, 0.5)) { - obj.detonate(client.character.characterId); - } - } - } - } - // Stop HUD timer if position is out of radius if ( client.hudTimer && @@ -1960,12 +1957,29 @@ export class ZonePacketHandlers { ) { return; } - - const y = packet.data.rotation1[1], - w = packet.data.rotation1[3], - yaw = Math.atan2(-w, y), - final = new Float32Array([yaw, 0, 0, 0]); - + const array = new Float32Array([ + packet.data.rotation1[3], + packet.data.rotation1[1], + packet.data.rotation2[2] + ]); + const matrix = quat2matrix(array); + const euler = [ + Math.atan2(matrix[7], matrix[8]), + Math.atan2( + -matrix[6], + Math.sqrt(Math.pow(matrix[7], 2) + Math.pow(matrix[8], 2)) + ), + Math.atan2(matrix[3], matrix[0]) + ]; + let final; + if (euler[0] >= 0) { + final = new Float32Array([euler[1], 0, 0, 0]); + } else { + final = new Float32Array([euler[2], 0, 0, 0]); + } + if (Math.abs(final[0]) < 0.01) { + final[0] = 0; + } const modelId = server.getItemDefinition( packet.data.itemDefinitionId )?.PLACEMENT_MODEL_ID; @@ -2772,7 +2786,7 @@ export class ZonePacketHandlers { if ( !isPosInRadius( - server.proximityItemsDistance + 0.2, // It doesn't always reach otherwise + server.proximityItemsDistance, client.character.state.position, sourceCharacter.state.position ) diff --git a/src/servers/ZoneServer2016/zoneserver.ts b/src/servers/ZoneServer2016/zoneserver.ts index aa4d7df89c..530215f3e9 100644 --- a/src/servers/ZoneServer2016/zoneserver.ts +++ b/src/servers/ZoneServer2016/zoneserver.ts @@ -104,9 +104,10 @@ import { getCurrentServerTimeWrapper, flhash, getDateString, + loadJson, chance, quat2heading, - isInsideSquare + getSimpleNpcCheckHidden } from "../../utils/utils"; import { Db, MongoClient, WithId } from "mongodb"; @@ -249,7 +250,6 @@ import { GatewayChannels } from "h1emu-core"; import { IngameTimeManager } from "./managers/gametimemanager"; import { H1z1ProtocolReadingFormat } from "types/protocols"; import { GatewayServer } from "../GatewayServer/gatewayserver"; -import { GatewayServerThreaded } from "../GatewayServer/gatewayserver.threaded"; import { WaterSource } from "./entities/watersource"; import { WebSocket } from "ws"; import { CommandHandler } from "./handlers/commands/commandhandler"; @@ -262,80 +262,40 @@ import { NavManager } from "../../utils/recast"; import { ProjectileEntity } from "./entities/projectileentity"; import { ChallengeManager, ChallengeType } from "./managers/challengemanager"; import { RandomEventsManager } from "./managers/randomeventsmanager"; -import { ExplosionManager } from "./managers/explosionmanager"; import { AiManager } from "./managers/aimanager"; import { AirdropManager } from "./managers/airdropmanager"; -import { PacketEncodingWorker } from "./managers/packetencodingworker"; //import { TaskManager } from "./managers/tasksmanager"; -const spawnLocations2 = PluginManager.loadServerData( - "2016/zoneData/Z1_gridSpawns.json" - ), - deprecatedDoors = PluginManager.loadServerData( - "2016/sampleData/deprecatedDoors.json" - ), - itemDefinitions = PluginManager.loadServerData( - "2016/dataSources/ServerItemDefinitions.json" - ), - containerDefinitions = PluginManager.loadServerData( - "2016/dataSources/ContainerDefinitions.json" - ), - profileDefinitions = PluginManager.loadServerData( - "2016/dataSources/ServerProfileDefinitions.json" - ), - projectileDefinitons = PluginManager.loadServerData( - "2016/dataSources/ServerProjectileDefinitions.json" - ), - itemClassDefinitions = PluginManager.loadServerData( - "2016/dataSources/ServerItemClassDefinitions.json" - ), - loadoutSlotItemClasses = PluginManager.loadServerData( - "2016/dataSources/LoadoutSlotItemClasses.json" - ), - equipSlotItemClasses = PluginManager.loadServerData( - "2016/dataSources/EquipSlotItemClasses.json" - ), - weaponDefinitions = PluginManager.loadServerData( - "2016/dataSources/ServerWeaponDefinitions" - ), - resourceDefinitions = PluginManager.loadServerData( - "2016/dataSources/Resources" - ), - Z1_POIs = PluginManager.loadServerData("2016/zoneData/Z1_POIs"), - hudIndicators = PluginManager.loadServerData( - "2016/dataSources/HudIndicators" - ), - screenEffects = PluginManager.loadServerData( - "2016/sampleData/screenEffects.json" - ), - clientEffectsDataSource = PluginManager.loadServerData( - "2016/dataSources/ClientEffects.json" - ), - itemUseOptionsDataSource = PluginManager.loadServerData( - "2016/dataSources/ItemUseOptions" - ), - gameRulesSource = PluginManager.loadServerData( - "2016/dataSources/ServerGameRules" - ), - models = PluginManager.loadServerData("2016/dataSources/Models"), - accountItemConversions = PluginManager.loadServerData( - "2016/dataSources/AcctItemConversions.json" - ), - rewardCrates = PluginManager.loadServerData( - "2016/dataSources/AccountCrates.json" - ), +const spawnLocations2 = require("../../../data/2016/zoneData/Z1_gridSpawns.json"), + deprecatedDoors = require("../../../data/2016/sampleData/deprecatedDoors.json"), + itemDefinitions = require("./../../../data/2016/dataSources/ServerItemDefinitions.json"), + containerDefinitions = require("./../../../data/2016/dataSources/ContainerDefinitions.json"), + profileDefinitions = require("./../../../data/2016/dataSources/ServerProfileDefinitions.json"), + projectileDefinitons = require("./../../../data/2016/dataSources/ServerProjectileDefinitions.json"), + itemClassDefinitions = require("./../../../data/2016/dataSources/ServerItemClassDefinitions.json"), + loadoutSlotItemClasses = require("./../../../data/2016/dataSources/LoadoutSlotItemClasses.json"), + equipSlotItemClasses = require("./../../../data/2016/dataSources/EquipSlotItemClasses.json"), + weaponDefinitions = require("../../../data/2016/dataSources/ServerWeaponDefinitions"), + resourceDefinitions = require("../../../data/2016/dataSources/Resources"), + Z1_POIs = require("../../../data/2016/zoneData/Z1_POIs"), + hudIndicators = require("../../../data/2016/dataSources/HudIndicators"), + screenEffects = require("../../../data/2016/sampleData/screenEffects.json"), + clientEffectsDataSource = require("../../../data/2016/dataSources/ClientEffects.json"), + itemUseOptionsDataSource = require("../../../data/2016/dataSources/ItemUseOptions"), + gameRulesSource = require("../../../data/2016/dataSources/ServerGameRules"), + models = require("../../../data/2016/dataSources/Models"), + accountItemConversions = require("./../../../data/2016/dataSources/AcctItemConversions.json"), + rewardCrates = require("./../../../data/2016/dataSources/AccountCrates.json"), equipmentModelTexturesMapping: Record< string, Record - > = PluginManager.loadServerData( - "2016/sampleData/equipmentModelTexturesMapping.json" - ); + > = require("../../../data/2016/sampleData/equipmentModelTexturesMapping.json"); export class ZoneServer2016 extends EventEmitter { /** Networking layer - allows sending game data to the game client, * lays on top of the H1Z1 protocol (on top of the actual H1Z1 packets) */ - _gatewayServer: GatewayServer | GatewayServerThreaded; + _gatewayServer: GatewayServer; readonly _protocol: H1Z1Protocol; _db!: Db; readonly _soloMode: boolean; @@ -346,36 +306,8 @@ export class ZoneServer2016 extends EventEmitter { _serverGuid = generateRandomGuid(); _worldId = 0; _grid: GridCell[] = []; - /** Flat lookup array for O(1) cell access: index = col * _gridNumCols + row */ - private _gridLookup: GridCell[] = []; - private _gridCellSize = 0; - private _gridOriginX = 0; - private _gridOriginZ = 0; - private _gridNumCols = 0; - private _gridNumRows = 0; _spawnGrid: SpawnCell[] = []; - /** Spatial hashes rebuilt every world tick to accelerate spawnCharacters/vehicleManager lookups. */ - private _charSpatialMap = new Map(); - private static readonly _CHAR_GRID_SIZE = 300; - - private static _charGridRange( - pos: Float32Array, - radius: number - ): [number, number, number, number] { - const sz = ZoneServer2016._CHAR_GRID_SIZE; - return [ - Math.floor((pos[0] - radius) / sz), - Math.floor((pos[0] + radius) / sz), - Math.floor((pos[2] - radius) / sz), - Math.floor((pos[2] + radius) / sz) - ]; - } - - /** Inverse observer index: entity characterId → clients that have it spawned. - * Maintained automatically by TrackedEntitySet in each client's spawnedEntities. */ - _entityObservers: Map> = new Map(); - saveTimeInterval: number = 600000; nextSaveTime: number = Date.now() + this.saveTimeInterval; @@ -466,7 +398,6 @@ export class ZoneServer2016 extends EventEmitter { airdropManager: AirdropManager; _ready: boolean = false; - private packetEncodingWorker?: PacketEncodingWorker; /** Information from ServerItemDefinitions.json */ _itemDefinitions: { [itemDefinitionId: number]: ItemDefinition } = @@ -501,10 +432,8 @@ export class ZoneServer2016 extends EventEmitter { private readonly _transientIdGenerator = generateTransientId(); enableWorldSaves: boolean; readonly gameVersion: GAME_VERSIONS = GAME_VERSIONS.H1Z1_6dec_2016; + isSaving: boolean = false; private _isSaving: boolean = false; - get isSaving(): boolean { - return this._isSaving; - } readonly worldSaveVersion: number = 2; enablePacketInputLogging: boolean = false; shutdownStartedTime: number = 0; @@ -514,6 +443,7 @@ export class ZoneServer2016 extends EventEmitter { isLocked: boolean = false; staticDTOs: Array = []; serverGameRules: string; + routinesLoopTimer?: NodeJS.Timeout; private _mongoClient?: MongoClient; rebootTimeTimer?: NodeJS.Timeout; inGameTimeManager: IngameTimeManager = new IngameTimeManager(); @@ -524,6 +454,7 @@ export class ZoneServer2016 extends EventEmitter { proximityItemsDistance!: number; interactionDistance!: number; charactersRenderDistance!: number; + tickRate!: number; worldRoutineRate!: number; welcomeMessage!: string; adminMessage!: string; @@ -540,17 +471,13 @@ export class ZoneServer2016 extends EventEmitter { damageWeapons!: boolean; disablePOIManager!: boolean; disableMapBoundsCheck!: boolean; - disableBaseCheck!: boolean; /* */ navManager: NavManager; - staticBuildings: AddSimpleNpc[] = PluginManager.loadServerData( - "2016/sampleData/staticbuildings.json" - ); + staticBuildings: AddSimpleNpc[] = require("../../../data/2016/sampleData/staticbuildings.json"); worldSaveFailed: boolean = false; challengeManager: ChallengeManager; challengePositionCheckInterval?: NodeJS.Timeout; randomEventsManager: RandomEventsManager; - explosionManager: ExplosionManager; gameMode: GameModes = GameModes.SURVIVAL; maxPacketLoss: number = 5; //tasksManager: TaskManager; @@ -566,7 +493,7 @@ export class ZoneServer2016 extends EventEmitter { ) { super(); this._clientProtocol = protocol; - this._gatewayServer = new GatewayServerThreaded(serverPort, gatewayKey); + this._gatewayServer = new GatewayServer(serverPort, gatewayKey); this._packetHandlers = new ZonePacketHandlers(); this._mongoAddress = mongoAddress; this._worldId = worldId || 0; @@ -593,7 +520,6 @@ export class ZoneServer2016 extends EventEmitter { this.navManager = new NavManager(); this.challengeManager = new ChallengeManager(this); this.randomEventsManager = new RandomEventsManager(this); - this.explosionManager = new ExplosionManager(this); //this.tasksManager = new TaskManager(); /* CONFIG MANAGER MUST BE INSTANTIATED LAST ! */ this.configManager = new ConfigManager(this, process.env.CONFIG_PATH); @@ -670,7 +596,7 @@ export class ZoneServer2016 extends EventEmitter { } const generatedTransient = this.getTransientId(characterId); const sessionId = - await this._gatewayServer.getSoeClientSessionId(soeClientId); + this._gatewayServer.getSoeClientSessionId(soeClientId); if (!sessionId) { return; } @@ -688,9 +614,8 @@ export class ZoneServer2016 extends EventEmitter { return; } if (!this._soloMode) { - const address = ( - await this._gatewayServer.getSoeClientNetworkInfos(soeClientId) - )?.address; + const address = + this._gatewayServer.getSoeClientNetworkInfos(soeClientId)?.address; if (!address) { return; } @@ -790,8 +715,8 @@ export class ZoneServer2016 extends EventEmitter { this.registerLoginConnectionListeners(internalServerPort); } - const dynamicappearance = PluginManager.loadServerData( - "2016/sampleData/dynamicappearance.json" + const dynamicappearance = loadJson( + __dirname + "/../../../data/2016/sampleData/dynamicappearance.json" ); this.dynamicappearance = dynamicappearance; if (this._mongoAddress && this.rebootTime) { @@ -991,34 +916,6 @@ export class ZoneServer2016 extends EventEmitter { this.fairPlayManager.defaultHashes = assetHashes.hashes; } break; - case "GlobalBroadcastForward": { - if ( - client.address !== this._loginServerInfo.address || - client.port !== this._loginServerInfo.port - ) { - debug( - `GlobalBroadcastForward rejected: unexpected sender ${client.address}:${client.port}` - ); - return; - } - const { - broadcastType, - initiatorName, - message, - originServerId, - rewardIds - } = packet.data; - debug( - `GlobalBroadcastForward type=${broadcastType} from server ${originServerId}` - ); - this.executeGlobalBroadcast( - broadcastType, - initiatorName, - message, - rewardIds - ); - break; - } default: debug(`Unhandled h1emu packet: ${packet.name}`); break; @@ -1031,15 +928,13 @@ export class ZoneServer2016 extends EventEmitter { async stop() { clearInterval(this.challengePositionCheckInterval); this.worldDataManager.kill(); - await this.worldObjectManager.stop(); - await this.explosionManager.stop(); this.inGameTimeManager.stop(); this.smeltingManager.clearTimers(); this.decayManager.clearTimers(); this.randomEventsManager.stop(); clearTimeout(this.worldRoutineTimer); clearTimeout(this.weatherManager.dynamicWorker); - clearTimeout(this._worldTickTimer); + clearTimeout(this.routinesLoopTimer); clearTimeout(this.rebootTimeTimer); if (this._loginConnectionManager) { await this._loginConnectionManager.stop(); @@ -1047,10 +942,6 @@ export class ZoneServer2016 extends EventEmitter { if (this._mongoClient) { await this._mongoClient.close(); } - if (this.packetEncodingWorker) { - await this.packetEncodingWorker.stop(); - this.packetEncodingWorker = undefined; - } await this._gatewayServer.stop(); await this.rconManager.stop(); } @@ -1060,7 +951,6 @@ export class ZoneServer2016 extends EventEmitter { if (this.abortShutdown) { this.abortShutdown = false; this.shutdownStarted = false; - this.shutdownStartedTime = 0; this.sendAlertToAll(`Server shutdown aborted.`); return; } @@ -1076,7 +966,6 @@ export class ZoneServer2016 extends EventEmitter { this.sendAlertToAll(`Server will shutdown now`); this.enableWorldSaves = false; if (!this.worldSaveFailed) { - this._isSaving = false; // allow shutdown save even if periodic save just ran try { await this.saveWorld(); } catch (e) { @@ -1150,41 +1039,10 @@ export class ZoneServer2016 extends EventEmitter { });*/ } - private static readonly _heavyPackets = new Set([ - "Weapon.Weapon", - "Construction.PlacementFinalizeRequest", - "Explosive.Explode", - "Command.Interact", - "Mount.MountRequest" - ]); - private static readonly MAX_HEAVY_PACKETS_PER_CLIENT_PER_SEC = 100; - onZoneDataEvent(client: Client, packet: H1z1ProtocolReadingFormat) { if (!client) { return; } - if (ZoneServer2016._heavyPackets.has(packet.name)) { - const now = Date.now(); - if (now - client.heavyPacketWindowStart > 1000) { - client.heavyPacketCount = 0; - client.heavyPacketWindowStart = now; - } - client.heavyPacketCount++; - if ( - client.heavyPacketCount > - ZoneServer2016.MAX_HEAVY_PACKETS_PER_CLIENT_PER_SEC - ) { - if ( - client.heavyPacketCount === - ZoneServer2016.MAX_HEAVY_PACKETS_PER_CLIENT_PER_SEC + 1 - ) { - console.log( - `[DDoS] Client ${client.character.characterId} heavy-packet rate-limited: ${client.heavyPacketCount} ${packet.name} in 1s (dropped)` - ); - } - return; - } - } if ( packet.name != "Command.ExecuteCommand" && packet.name != "H1emu.RequestModules" && @@ -1264,9 +1122,7 @@ export class ZoneServer2016 extends EventEmitter { try { const characterData = JSON.parse(characterObjStringify), characterModelData = getCharacterModelData(characterData.payload); - let character: FullCharacterSaveData = PluginManager.loadServerData( - "2016/sampleData/character.json" - ); + let character: FullCharacterSaveData = require("../../../data/2016/sampleData/character.json"); character = { ...character, serverId: characterData.serverId, @@ -1340,18 +1196,6 @@ export class ZoneServer2016 extends EventEmitter { async onClientAllowedRequest(client: LZConnectionClient, packet: any) { const { characterId, loginSessionId, reqId } = packet.data; - if (!this._ready) { - console.log( - `Character (${characterId}) connection rejected: server is not ready.` - ); - this.sendCharacterAllowedReply( - client, - reqId, - false, - CONNECTION_REJECTION_FLAGS.SERVER_LOCKED - ); - return; - } if (this.isRebooting) { console.log( `Character (${characterId}) connection rejected due to reboot` @@ -1537,6 +1381,15 @@ export class ZoneServer2016 extends EventEmitter { ) { continue; } + if ( + object.checkBuildingObstruct( + this, + client.character.state.position, + object.getParentFoundation(this) + ) + ) { + continue; + } } const container = object.getContainer(); if (container) { @@ -1698,7 +1551,7 @@ export class ZoneServer2016 extends EventEmitter { /** * Caches item definitons so they aren't packed every time a client logs in. */ - private async packItemDefinitions() { + private packItemDefinitions() { /* this.itemDefinitionsCache = this._protocol.pack("Command.ItemDefinitions", { data: { @@ -1772,7 +1625,7 @@ export class ZoneServer2016 extends EventEmitter { } }); - const itemDefinitionsCache = await this.packPacket( + const itemDefinitionsCache = this._protocol.pack( "Command.ItemDefinitions", { data: { @@ -1784,8 +1637,8 @@ export class ZoneServer2016 extends EventEmitter { this.itemDefinitionsCache = itemDefinitionsCache; } - private async packDynamicAppearance() { - const dynamicAppearanceCache = await this.packPacket( + private packDynamicAppearance() { + const dynamicAppearanceCache = this._protocol.pack( "ReferenceData.DynamicAppearance", { ITEM_APPEARANCE_DEFINITIONS: @@ -1802,8 +1655,8 @@ export class ZoneServer2016 extends EventEmitter { /** * Caches weapon definitons so they aren't packed every time a client logs in. */ - private async packWeaponDefinitions() { - const weaponDefinitionsCache = await this.packPacket( + private packWeaponDefinitions() { + const weaponDefinitionsCache = this._protocol.pack( "ReferenceData.WeaponDefinitions", { data: { @@ -1837,8 +1690,8 @@ export class ZoneServer2016 extends EventEmitter { /** * Caches projectile definitons so they aren't packed every time a client logs in. */ - private async packProjectileDefinitions() { - const projectileDefinitionsCache = await this.packPacket( + private packProjectileDefinitions() { + const projectileDefinitionsCache = this._protocol.pack( "ReferenceData.ProjectileDefinitions", { definitionsData: projectileDefinitons @@ -1852,8 +1705,8 @@ export class ZoneServer2016 extends EventEmitter { /** * Caches profile definitons so they aren't packed every time a client logs in. */ - private async packProfileDefinitions() { - const profileDefinitionsCache = await this.packPacket( + private packProfileDefinitions() { + const profileDefinitionsCache = this._protocol.pack( "ReferenceData.ProfileDefinitions", { data: { @@ -1869,8 +1722,8 @@ export class ZoneServer2016 extends EventEmitter { /** * Caches itemClass definitons so they aren't packed every time a client logs in. */ - private async packitemClassDefinitions() { - const itemClassDefinitionsCache = await this.packPacket( + private packitemClassDefinitions() { + const itemClassDefinitionsCache = this._protocol.pack( "ReferenceData.ItemClassDefinitions", { ITEMCLASS_DEFINITIONS: Object.values(itemClassDefinitions).map( @@ -2072,18 +1925,17 @@ export class ZoneServer2016 extends EventEmitter { // !!ANYTHING THAT USES / GENERATES ITEMS MUST BE CALLED AFTER WORLD DATA IS LOADED!! //this.packItemDefinitions(); // No longer necessary, we'll see if only sending the required items will impact server performance - await this.packWeaponDefinitions(); - await this.packProjectileDefinitions(); - await this.packProfileDefinitions(); - await this.packitemClassDefinitions(); - await this.packDynamicAppearance(); + this.packWeaponDefinitions(); + this.packProjectileDefinitions(); + this.packProfileDefinitions(); + this.packitemClassDefinitions(); + this.packDynamicAppearance(); this.worldObjectManager.createDoors(this); this.worldObjectManager.createProps(this); await this.pluginManager.initializePlugins(this); - this.worldObjectManager.lootTableManager.load(); - await this.customizeStaticDTOs(); + this.customizeStaticDTOs(); /*this.tasksManager.register_schedule( this.worldRoutine.bind(this), @@ -2096,7 +1948,6 @@ export class ZoneServer2016 extends EventEmitter { );*/ this._ready = true; - console.log("Server is ready and accepting connections."); console.log( `Server saving ${this.enableWorldSaves ? "enabled" : "disabled"}.` ); @@ -2121,15 +1972,13 @@ export class ZoneServer2016 extends EventEmitter { this._worldId ); const worldConstructions: LootableConstructionSaveData[] = []; - let saveI = 0; - for (const entity of Object.values(this._worldLootableConstruction)) { - if (++saveI % 100 === 0) await scheduler.yield(); + Object.values(this._worldLootableConstruction).forEach((entity) => { if ( entity.parentObjectCharacterId == this._serverGuid || entity instanceof WaterSource || (entity instanceof TrapEntity && entity?.worldOwned) ) - continue; // Don't save world spawned campfires / barbeques + return; // Don't save world spawned campfires / barbeques const lootableConstructionSaveData = WorldDataManager.getLootableConstructionSaveData( entity, @@ -2137,11 +1986,10 @@ export class ZoneServer2016 extends EventEmitter { ); removeUntransferableFields(lootableConstructionSaveData); worldConstructions.push(lootableConstructionSaveData); - } + }); const constructions: ConstructionParentSaveData[] = []; - saveI = 0; - for (const entity of Object.values(this._constructionFoundations)) { - if (++saveI % 100 === 0) await scheduler.yield(); + + Object.values(this._constructionFoundations).forEach((entity) => { if (entity.itemDefinitionId != Items.FOUNDATION_EXPANSION) { const construction = WorldDataManager.getConstructionParentSaveData( entity, @@ -2150,28 +1998,26 @@ export class ZoneServer2016 extends EventEmitter { // isTransferable(construction) too complex will run on max recursive call error constructions.push(construction); } - } + }); const crops: PlantingDiameterSaveData[] = []; - for (const entity of Object.values(this._temporaryObjects)) { + Object.values(this._temporaryObjects).forEach((entity) => { if (entity instanceof PlantingDiameter) { crops.push( WorldDataManager.getPlantingDiameterSaveData(entity, this._worldId) ); } - } + }); const traps: TrapSaveData[] = []; - saveI = 0; - for (const entity of Object.values(this._traps)) { - if (++saveI % 100 === 0) await scheduler.yield(); + Object.values(this._traps).forEach((entity) => { if (entity instanceof TrapEntity && !entity.worldOwned) { traps.push(WorldDataManager.getTrapSaveData(entity, this._worldId)); } - } - for (const entity of Object.values(this._explosives)) { + }); + Object.values(this._explosives).forEach((entity) => { if (entity instanceof ExplosiveEntity && entity.isLandmine()) { traps.push(WorldDataManager.getTrapSaveData(entity, this._worldId)); } - } + }); console.timeEnd("ZONE: processing"); @@ -2269,7 +2115,6 @@ export class ZoneServer2016 extends EventEmitter { if (!(await this.hookManager.checkAsyncHook("OnServerInit"))) return; await this.setupServer(); - this.initializePacketEncodingWorker(); if (this.isPvE) { console.log("Server in PvE mode"); } @@ -2278,6 +2123,7 @@ export class ZoneServer2016 extends EventEmitter { if (this.isSurvival()) { this.speedtreeManager.initiateList(); } + this.startRoutinesLoop(); if (this.isSurvival()) { this.smeltingManager.checkSmeltables(this); this.smeltingManager.checkCollectors(this); @@ -2298,7 +2144,6 @@ export class ZoneServer2016 extends EventEmitter { ); } this._gatewayServer.start(); - this.startWorldTick(); this.worldRoutineTimer = setTimeout( () => this.worldRoutine.bind(this)(), this.worldRoutineRate @@ -2427,7 +2272,7 @@ export class ZoneServer2016 extends EventEmitter { }); }*/ if (!this.weaponDefinitionsCache) { - await this.packWeaponDefinitions(); + this.packWeaponDefinitions(); } if (this.weaponDefinitionsCache) { this.sendRawDataReliable(client, this.weaponDefinitionsCache); @@ -2454,7 +2299,7 @@ export class ZoneServer2016 extends EventEmitter { // Z1BR resolved this issue by using LZ4 to compress this block. We can easily add this in the patch, but we'll implement it if users start experiencing network issues. // FYI: This was tested with 40 players on my server without any issues. - Jason if (!this.dynamicAppearanceCache) { - await this.packDynamicAppearance(); + this.packDynamicAppearance(); } if (this.dynamicAppearanceCache) { this.sendRawDataReliable(client, this.dynamicAppearanceCache); @@ -2503,170 +2348,65 @@ export class ZoneServer2016 extends EventEmitter { const grid = []; for (let i = -mapWidth / 2; i < mapWidth / 2; i += gridCellSize) { for (let j = -mapHeight / 2; j < mapHeight / 2; j += gridCellSize) { - const cell = new GridCell(this, i, j, gridCellSize, gridCellSize); + const cell = new GridCell(i, j, gridCellSize, gridCellSize); grid.push(cell); } } return grid; } - /** Builds a flat O(1) lookup from the uniform grid so getVisibleCells can - * compute the candidate cell range directly instead of iterating all cells. */ - private buildGridLookup() { - if (this._grid.length === 0) return; - const cs = this._grid[0].width; - let minX = Infinity, - minZ = Infinity, - maxX = -Infinity, - maxZ = -Infinity; - for (const cell of this._grid) { - if (cell.position[0] < minX) minX = cell.position[0]; - if (cell.position[2] < minZ) minZ = cell.position[2]; - if (cell.position[0] > maxX) maxX = cell.position[0]; - if (cell.position[2] > maxZ) maxZ = cell.position[2]; - } - const numCols = Math.round((maxX - minX) / cs) + 1; - const numRows = Math.round((maxZ - minZ) / cs) + 1; - this._gridCellSize = cs; - this._gridOriginX = minX; - this._gridOriginZ = minZ; - this._gridNumCols = numCols; - this._gridNumRows = numRows; - this._gridLookup = new Array(numCols * numRows); - for (const cell of this._grid) { - const col = Math.round((cell.position[0] - minX) / cs); - const row = Math.round((cell.position[2] - minZ) / cs); - this._gridLookup[col * numCols + row] = cell; - } - } - - /** Rebuilds _gridLookup after divideLargeCells, where cell sizes are no longer - * uniform. Uses the smallest cell size so every cell maps to a valid slot. */ - private buildGridLookupMixed() { - if (this._grid.length === 0) return; - let minX = Infinity, - minZ = Infinity, - maxX = -Infinity, - maxZ = -Infinity, - cs = Infinity; - for (const cell of this._grid) { - if (cell.position[0] < minX) minX = cell.position[0]; - if (cell.position[2] < minZ) minZ = cell.position[2]; - if (cell.position[0] > maxX) maxX = cell.position[0]; - if (cell.position[2] > maxZ) maxZ = cell.position[2]; - if (cell.width < cs) cs = cell.width; - } - const numCols = Math.round((maxX - minX) / cs) + 1; - const numRows = Math.round((maxZ - minZ) / cs) + 1; - this._gridCellSize = cs; - this._gridOriginX = minX; - this._gridOriginZ = minZ; - this._gridNumCols = numCols; - this._gridNumRows = numRows; - this._gridLookup = new Array(numCols * numRows); - for (const cell of this._grid) { - // A large cell covers multiple lookup slots — fill all of them - const cols = Math.round(cell.width / cs); - const rows = Math.round(cell.height / cs); - const startCol = Math.round((cell.position[0] - minX) / cs); - const startRow = Math.round((cell.position[2] - minZ) / cs); - for (let c = 0; c < cols; c++) { - for (let r = 0; r < rows; r++) { - this._gridLookup[(startCol + c) * numCols + (startRow + r)] = cell; - } - } - } - } - divideLargeCells(threshold: number) { const grid = this._grid; - let didSplit = false; for (let i = 0; i < grid.length; i++) { const gridCell: GridCell = grid[i]; if (gridCell.height < 250) continue; if (gridCell.objects.length > threshold) { const newGridCellWidth = gridCell.width / 2; const newGridCellHeight = gridCell.height / 2; - const x0 = gridCell.position[0]; - const z0 = gridCell.position[2]; // 4 cells made of 1 const newGridCell1 = new GridCell( - this, - x0, - z0, + gridCell.position[0], + gridCell.position[2], newGridCellWidth, newGridCellHeight ); const newGridCell2 = new GridCell( - this, - x0 + newGridCellWidth, - z0, + gridCell.position[0] + newGridCellWidth, + gridCell.position[2], newGridCellWidth, newGridCellHeight ); const newGridCell3 = new GridCell( - this, - x0, - z0 + newGridCellHeight, + gridCell.position[0], + gridCell.position[2] + newGridCellHeight, newGridCellWidth, newGridCellHeight ); const newGridCell4 = new GridCell( - this, - x0 + newGridCellWidth, - z0 + newGridCellHeight, + gridCell.position[0] + newGridCellWidth, + gridCell.position[2] + newGridCellHeight, newGridCellWidth, newGridCellHeight ); - - // Swap-and-pop: O(1) removal instead of splice O(N) - const objects = gridCell.objects; - grid[i] = grid[grid.length - 1]; - grid.pop(); + // remove old grid cell + const objects = this._grid[i].objects; + this._grid.splice(i, 1); i--; - grid.push(newGridCell1); - grid.push(newGridCell2); - grid.push(newGridCell3); - grid.push(newGridCell4); - - // Place each object directly into one of the 4 new cells rather than - // calling pushToGridCell, which would use the now-stale _gridLookup. - const newCells = [ - newGridCell1, - newGridCell2, - newGridCell3, - newGridCell4 - ]; - for (const obj of objects) { - const px = obj.state.position[0]; - const pz = obj.state.position[2]; - for (const cell of newCells) { - if ( - px >= cell.position[0] && - px <= cell.position[0] + cell.width && - pz >= cell.position[2] && - pz <= cell.position[2] + cell.height - ) { - cell.objects.push(obj); - setImmediate(() => this.onEntityAddedToCell(obj, cell)); - break; - } - } - } - - // Rebuild lookup once at the end — flag here, rebuild after the loop. - didSplit = true; + this._grid.push(newGridCell1); + this._grid.push(newGridCell2); + this._grid.push(newGridCell3); + this._grid.push(newGridCell4); + objects.forEach((object: BaseEntity) => { + this.pushToGridCell(object); + }); } } - if (didSplit) this.buildGridLookupMixed(); } pushToGridCell(obj: BaseEntity) { - if (this._grid.length == 0) { + if (this._grid.length == 0) this._grid = this.divideMapIntoGrid(8196, 8196, 250); - this.buildGridLookup(); - } if ( obj instanceof Vehicle || obj instanceof Character || @@ -2677,51 +2417,39 @@ export class ZoneServer2016 extends EventEmitter { // dont push objects that can change its position return; } - const pos = obj.state.position; - const cs = this._gridCellSize; - const col = Math.max( - 0, - Math.min( - this._gridNumCols - 1, - Math.floor((pos[0] - this._gridOriginX) / cs) - ) - ); - const row = Math.max( - 0, - Math.min( - this._gridNumRows - 1, - Math.floor((pos[2] - this._gridOriginZ) / cs) - ) - ); - const gridCell = this._gridLookup[col * this._gridNumCols + row]; - if (!gridCell || gridCell.objects.includes(obj)) return; - gridCell.objects.push(obj); - setImmediate(() => this.onEntityAddedToCell(obj, gridCell)); + for (let i = 0; i < this._grid.length; i++) { + const gridCell = this._grid[i]; + if ( + obj.state.position[0] >= gridCell.position[0] && + obj.state.position[0] <= gridCell.position[0] + gridCell.width && + obj.state.position[2] >= gridCell.position[2] && + obj.state.position[2] <= gridCell.position[2] + gridCell.height + ) { + if (gridCell.objects.includes(obj)) { + return; + } + gridCell.objects.push(obj); + } + } } assignChunkRenderDistance(client: Client) { - if (this._gridLookup.length === 0) { - client.chunkRenderDistance = 700; - return; + let lowerRenderDistance = false; + const character = client.character; + for (let i = 0; i < this._grid.length; i++) { + const gridCell: GridCell = this._grid[i]; + + if ( + character.state.position[0] >= gridCell.position[0] && + character.state.position[0] <= gridCell.position[0] + gridCell.width && + character.state.position[2] >= gridCell.position[2] && + character.state.position[2] <= gridCell.position[2] + gridCell.height && + gridCell.height < 250 + ) { + lowerRenderDistance = true; + } } - const pos = client.character.state.position; - const cs = this._gridCellSize; - const col = Math.max( - 0, - Math.min( - this._gridNumCols - 1, - Math.floor((pos[0] - this._gridOriginX) / cs) - ) - ); - const row = Math.max( - 0, - Math.min( - this._gridNumRows - 1, - Math.floor((pos[2] - this._gridOriginZ) / cs) - ) - ); - const cell = this._gridLookup[col * this._gridNumCols + row]; - client.chunkRenderDistance = cell && cell.height < 250 ? 600 : 700; + client.chunkRenderDistance = lowerRenderDistance ? 600 : 700; } private async worldRoutine() { @@ -2730,16 +2458,18 @@ export class ZoneServer2016 extends EventEmitter { if (this._ready) { this.constructionManager.plantManager(this); await scheduler.yield(); - await this.worldObjectManager.run(this); + this.worldObjectManager.run(this); await scheduler.yield(); this.checkVehiclesInMapBounds(); await scheduler.yield(); this.updateSyncTeleport(); await scheduler.yield(); + this.setTickRate(); + await scheduler.yield(); this.updateSpectatorMap(); if ( this.enableWorldSaves && - !this._isSaving && + !this.isSaving && this.nextSaveTime - Date.now() < 0 ) { this.saveWorld(); @@ -2749,16 +2479,24 @@ export class ZoneServer2016 extends EventEmitter { this.worldRoutineTimer.refresh(); } + setTickRate() { + const size = _.size(this._clients); + if (size <= 0) { + this.tickRate = 3000; + return; + } + this.tickRate = 3000 / size; + } + async deleteClient(client: Client) { this.hookManager.checkHook("OnPlayerDisconnected", client); if (!client) { + this.setTickRate(); return; } if (client.afkTimer) { clearInterval(client.afkTimer); } - clearTimeout(client.pingTimer); - clearTimeout(client.fairPlayTimer); if (client.assetIntegrityKickTimer) { clearTimeout(client.assetIntegrityKickTimer); @@ -2801,16 +2539,12 @@ export class ZoneServer2016 extends EventEmitter { this.groupManager.handlePlayerDisconnect(this, client); } } - for (const cell of client.subscribedCells) { - cell.subscribers.delete(client); - } - client.subscribedCells.clear(); - client.spawnedEntities.clear(); // remove this client from all _entityObservers entries delete this._clients[client.sessionId]; - await this._gatewayServer.deleteSoeClient(client.soeClientId); + this._gatewayServer.deleteSoeClient(client.soeClientId); if (!this._soloMode) { this.sendZonePopulationUpdate(); } + this.setTickRate(); this.airdropManager.sendDeliveryStatus(); } @@ -2830,19 +2564,23 @@ export class ZoneServer2016 extends EventEmitter { targetPing = 0; if (sourceClient && !targetClient) { sourceName = sourceClient.character.name || "Unknown"; - const sourceSOEClientAvgPing = - await this._gatewayServer.getSoeClientAvgPing(sourceClient.soeClientId); + const sourceSOEClientAvgPing = this._gatewayServer.getSoeClientAvgPing( + sourceClient.soeClientId + ); sourcePing = sourceSOEClientAvgPing ?? 0; } else if (!sourceClient && targetClient) { targetName = targetClient.character.name || "Unknown"; - const targetSOEClientAvgPing = - await this._gatewayServer.getSoeClientAvgPing(targetClient.soeClientId); + const targetSOEClientAvgPing = this._gatewayServer.getSoeClientAvgPing( + targetClient.soeClientId + ); targetPing = targetSOEClientAvgPing ?? 0; } else if (sourceClient && targetClient) { - const sourceSOEClientAvgPing = - await this._gatewayServer.getSoeClientAvgPing(sourceClient.soeClientId); - const targetSOEClientAvgPing = - await this._gatewayServer.getSoeClientAvgPing(targetClient.soeClientId); + const sourceSOEClientAvgPing = this._gatewayServer.getSoeClientAvgPing( + sourceClient.soeClientId + ); + const targetSOEClientAvgPing = this._gatewayServer.getSoeClientAvgPing( + targetClient.soeClientId + ); sourcePing = sourceSOEClientAvgPing ?? 0; sourceName = sourceClient.character.name || "Unknown"; targetName = targetClient.character.name || "Unknown"; @@ -2886,11 +2624,8 @@ export class ZoneServer2016 extends EventEmitter { { recipesDiscovered: client.character.metrics.recipesDiscovered, zombiesKilled: client.character.metrics.zombiesKilled, - minutesSurvived: Math.max( - 0, - Math.ceil( - (Date.now() - client.character.metrics.startedSurvivingTP) / 60000 - ) + minutesSurvived: Math.ceil( + (Date.now() - client.character.metrics.startedSurvivingTP) / 60000 ), wildlifeKilled: client.character.metrics.wildlifeKilled, vehiclesDestroyed: client.character.metrics.vehiclesDestroyed, @@ -3247,53 +2982,44 @@ export class ZoneServer2016 extends EventEmitter { if (sourceIsProjectile) { if (sourceEntity.itemDefinitionId == Items.WEAPON_MOLOTOV) { - if (!this.isPvE) { - const molotovPos = sourceEntity.state.position; - const [cx0, cx1, cz0, cz1] = ZoneServer2016._charGridRange( - molotovPos, - 5 - ); - for (let cx = cx0; cx <= cx1; cx++) { - for (let cz = cz0; cz <= cz1; cz++) { - const bucket = this._charSpatialMap.get(`${cx},${cz}`); - if (!bucket) continue; - for (const c of bucket) { - const character = c.character; - if ( - getDistance(character.state.position, molotovPos) <= 5 && - !character.characterStates.inWater - ) { - this.applyCharacterEffect( - character, - Effects.PFX_Fire_Person_loop, - 700, - 10000 - ); - } - } - } + for (const characterId in this._characters) { + if (this.isPvE) { + break; + } + const character = this._characters[characterId]; + if ( + getDistance( + character.state.position, + sourceEntity.state.position + ) <= 5 && + !character.characterStates.inWater + ) { + this.applyCharacterEffect( + character, + Effects.PFX_Fire_Person_loop, + 700, + 10000 + ); } } return; } if (sourceEntity.itemDefinitionId == Items.GRENADE_FLASH) { - if (!this.isPvE) { - const flashPos = sourceEntity.state.position; - const [cx0, cx1, cz0, cz1] = ZoneServer2016._charGridRange( - flashPos, - 12 - ); - for (let cx = cx0; cx <= cx1; cx++) { - for (let cz = cz0; cz <= cz1; cz++) { - const bucket = this._charSpatialMap.get(`${cx},${cz}`); - if (!bucket) continue; - for (const c of bucket) { - if (getDistance(c.character.state.position, flashPos) <= 12) { - this.addScreenEffect(c, this._screenEffects["FLASH"]); - this.sendAnimationToAllWithSpawnedEntity(c.character, 9); - } - } - } + for (const a in this._clients) { + if (this.isPvE) { + break; + } + const c = this._clients[a]; + const character = c.character; + if ( + getDistance( + character.state.position, + sourceEntity.state.position + ) <= 12 + ) { + this.addScreenEffect(c, this._screenEffects["FLASH"]); + + this.sendAnimationToAllWithSpawnedEntity(c.character, 9); } } return; @@ -3303,7 +3029,10 @@ export class ZoneServer2016 extends EventEmitter { // render distance is max client.chunkRenderDistance, could probably be lowered a lot // - meme - for (const gridCell of this.getGridCellsInRadius(position, 400)) { + for (const gridCell of this._grid) { + if (!isPosInRadius(400, gridCell.position, position)) { + continue; + } for (const object of gridCell.objects) { object.OnExplosiveHit(this, sourceEntity, client); } @@ -3313,19 +3042,18 @@ export class ZoneServer2016 extends EventEmitter { // these entities do not use the grid system - { - const expPos = sourceEntity.state.position; - const [cx0, cx1, cz0, cz1] = ZoneServer2016._charGridRange(expPos, 5); - for (let cx = cx0; cx <= cx1; cx++) { - for (let cz = cz0; cz <= cz1; cz++) { - const bucket = this._charSpatialMap.get(`${cx},${cz}`); - if (!bucket) continue; - for (const c of bucket) { - if (isPosInRadiusWithY(5, c.character.state.position, expPos, 3)) - c.character.OnExplosiveHit(this, sourceEntity); - } - } - } + for (const characterId in this._characters) { + const character = this._characters[characterId]; + if ( + !isPosInRadiusWithY( + 5, + character.state.position, + sourceEntity.state.position, + 3 + ) + ) + continue; + character.OnExplosiveHit(this, sourceEntity); } for (const vehicleKey in this._vehicles) { const vehicle = this._vehicles[vehicleKey]; @@ -4072,12 +3800,12 @@ export class ZoneServer2016 extends EventEmitter { if (!weaponItem) return; const entity = this.getEntity(hitReport.characterId); if (weaponItem.itemDefinitionId == Items.WEAPON_BOW_RECURVE) { - const projectile = Object.values(this._throwableProjectiles).find( - (p) => p.projectileUniqueId === fireHint.projectileUniqueId - ); - if (projectile) { - projectile.applyPostion(packet.hitReport.position); - projectile.onTrigger(this); + for (const a in this._throwableProjectiles) { + const projectile = this._throwableProjectiles[a] as ProjectileEntity; + if (projectile.projectileUniqueId == fireHint.projectileUniqueId) { + projectile.applyPostion(packet.hitReport.position); + projectile.onTrigger(this); + } } return; } @@ -4231,7 +3959,7 @@ export class ZoneServer2016 extends EventEmitter { } } - async customizeStaticDTOs() { + customizeStaticDTOs() { // caches DTOs that should always be removed for (const object in this._lootableProps) { @@ -4273,7 +4001,7 @@ export class ZoneServer2016 extends EventEmitter { }; this.staticDTOs.push(DTOinstance); }); - const cache = await this.packPacket("DtoObjectInitialData", { + const cache = this._protocol.pack("DtoObjectInitialData", { unknownDword1: 1, unknownArray1: this.staticDTOs, unknownArray2: [{}] @@ -4324,82 +4052,13 @@ export class ZoneServer2016 extends EventEmitter { }); } - /** - * Deletes multiple entities from the same dictionary in one optimised pass. - */ - batchDeleteEntities( - characterIds: string[], - dictionary: EntityDictionary - ): void { - if (characterIds.length === 0) return; - - const entities: Array<{ id: string; entity: BaseEntity }> = []; - for (const id of characterIds) { - const entity = dictionary[id]; - if (entity) entities.push({ id, entity }); - } - if (entities.length === 0) return; - - // 1. Pack + send Character.RemovePlayer once per entity - for (const { id } of entities) { - this.sendDataToAllWithSpawnedEntity( - dictionary, - id, - "Character.RemovePlayer", - { - characterId: id, - unknownWord1: 0, - effectId: 0, - timeToDisappear: 0, - effectDelay: 0 - } - ); - } - - // 2. ONE grid pass to remove all entities at once - const entitySet = new Set(entities.map((e) => e.entity)); - for (const cell of this._grid) { - if (cell.objects.length === 0) continue; - for (let i = cell.objects.length - 1; i >= 0; i--) { - if (entitySet.has(cell.objects[i])) cell.objects.splice(i, 1); - } - } - - // 3. ONE client pass: clean spawnedEntities, collect affected clients - const affectedClients = new Set(); - for (const a in this._clients) { - const client = this._clients[a]; - for (const { entity } of entities) { - if (client.spawnedEntities.delete(entity)) affectedClients.add(client); - } - } - - // 4. ONE ProximateItems per affected client (not one per deletion) - for (const client of affectedClients) { - this.sendData( - client, - "ClientUpdate.ProximateItems", - this.getProximityItems(client) - ); - } - - // 5. Clean up registries - for (const { id, entity } of entities) { - this.aiManager.removeEntity(entity); - delete dictionary[id]; - delete this._transientIds[this._characterIds[id]]; - delete this._characterIds[id]; - } - } - deleteEntity( characterId: string, dictionary: EntityDictionary, effectId?: number, timeToDisappear?: number ): boolean { - const entity = dictionary[characterId]; - if (!entity) return false; + if (!dictionary[characterId]) return false; this.sendDataToAllWithSpawnedEntity( dictionary, characterId, @@ -4413,8 +4072,8 @@ export class ZoneServer2016 extends EventEmitter { } ); this._grid.forEach((cell: GridCell) => { - if (cell.objects.includes(entity)) { - cell.objects.splice(cell.objects.indexOf(entity), 1); + if (cell.objects.includes(dictionary[characterId])) { + cell.objects.splice(cell.objects.indexOf(dictionary[characterId]), 1); } }); @@ -4422,15 +4081,15 @@ export class ZoneServer2016 extends EventEmitter { for (const a in this._clients) { const client = this._clients[a]; - if (client.spawnedEntities.delete(entity)) { + if (client.spawnedEntities.delete(dictionary[characterId])) { this.sendData( client, "ClientUpdate.ProximateItems", this.getProximityItems(client) ); } + this.aiManager.removeEntity(dictionary[characterId]); } - this.aiManager.removeEntity(entity); delete dictionary[characterId]; delete this._transientIds[this._characterIds[characterId]]; delete this._characterIds[characterId]; @@ -4460,7 +4119,22 @@ export class ZoneServer2016 extends EventEmitter { this.sendReplicationData(client, entity); } addSimpleNpc(client: Client, entity: BaseSimpleNpc) { - this.sendData(client, "AddSimpleNpc", entity.pGetSimpleNpc()); + if ( + entity instanceof LootableConstructionEntity || + entity instanceof ConstructionChildEntity + ) { + this.sendData( + client, + "AddSimpleNpc", + getSimpleNpcCheckHidden(this, client, entity) + ); + } else { + this.sendData( + client, + "AddSimpleNpc", + entity.pGetSimpleNpc() + ); + } this.sendReplicationData(client, entity); } @@ -4507,41 +4181,30 @@ export class ZoneServer2016 extends EventEmitter { } spawnCharacters(client: Client) { - const pos = client.character.state.position; - const renderDist = this.charactersRenderDistance; - const [cellMinX, cellMaxX, cellMinZ, cellMaxZ] = - ZoneServer2016._charGridRange(pos, renderDist); - - for (let cx = cellMinX; cx <= cellMaxX; cx++) { - for (let cz = cellMinZ; cz <= cellMaxZ; cz++) { - const bucket = this._charSpatialMap.get(`${cx},${cz}`); - if (!bucket) continue; - for (const c of bucket) { - const characterObj = c.character; - if ( - client.character.characterId !== characterObj.characterId && - isPosInRadius( - characterObj.npcRenderDistance || renderDist, - pos, - characterObj.state.position - ) && - !client.spawnedEntities.has(characterObj) && - characterObj.isAlive && - !characterObj.isSpectator && - !characterObj.isVanished && - (characterObj.isHidden === client.character.isHidden || - client.character.isSpectator) - ) { - this.sendData( - client, - "AddLightweightPc", - characterObj.pGetLightweightPC(this, c) - ); - client.spawnedEntities.add( - this._characters[characterObj.characterId] - ); - } - } + for (const c in this._clients) { + const characterObj: Character = this._clients[c].character; + if ( + client.character.characterId != characterObj.characterId && + characterObj.isReady && + isPosInRadius( + characterObj.npcRenderDistance || this.charactersRenderDistance, + client.character.state.position, + characterObj.state.position + ) && + !client.spawnedEntities.has(characterObj) && + characterObj.isAlive && + !characterObj.isSpectator && + !characterObj.isVanished && + (characterObj.isHidden == client.character.isHidden || + client.character.isSpectator) /* && + client.banType != "hiddenplayers"*/ + ) { + this.sendData( + client, + "AddLightweightPc", + characterObj.pGetLightweightPC(this, this._clients[c]) + ); + client.spawnedEntities.add(this._characters[characterObj.characterId]); } } } @@ -4549,26 +4212,23 @@ export class ZoneServer2016 extends EventEmitter { spawnCharacterToOtherClients(character: Character) { const client = this.getClientByCharId(character.characterId); if (!client) return; - const pos = character.state.position; - const renderDist = - character.npcRenderDistance || this.charactersRenderDistance; - const [cellMinX, cellMaxX, cellMinZ, cellMaxZ] = - ZoneServer2016._charGridRange(pos, renderDist); - const pcData = character.pGetLightweightPC(this, client); - for (let cx = cellMinX; cx <= cellMaxX; cx++) { - for (let cz = cellMinZ; cz <= cellMaxZ; cz++) { - const bucket = this._charSpatialMap.get(`${cx},${cz}`); - if (!bucket) continue; - for (const c of bucket) { - if ( - c.character !== character && - !c.spawnedEntities.has(character) && - isPosInRadius(renderDist, pos, c.character.state.position) - ) { - this.sendData(c, "AddLightweightPc", pcData); - c.spawnedEntities.add(character); - } - } + for (const a in this._clients) { + const c = this._clients[a]; + if ( + isPosInRadius( + character.npcRenderDistance || this.charactersRenderDistance, + character.state.position, + c.character.state.position + ) && + !c.spawnedEntities.has(character) && + character != c.character + ) { + this.sendData( + c, + "AddLightweightPc", + character.pGetLightweightPC(this, client) + ); + c.spawnedEntities.add(character); } } } @@ -4596,250 +4256,159 @@ export class ZoneServer2016 extends EventEmitter { } } - // ── Event-driven cell subscription system ────────────────────────── - - /** Returns the set of grid cells visible to the client from their current position. - * Uses a direct index range into _gridLookup instead of iterating all cells. */ - private getVisibleCells(client: Client): Set { - const pos = client.character.state.position; - const r = client.chunkRenderDistance; - const result = new Set(); - - if (this._gridLookup.length === 0) return result; - - const cs = this._gridCellSize; - const numCols = this._gridNumCols; - const numRows = this._gridNumRows; - const colMin = Math.max( - 0, - Math.floor((pos[0] - r - this._gridOriginX) / cs) - ); - const colMax = Math.min( - numCols - 1, - Math.floor((pos[0] + r - this._gridOriginX) / cs) - ); - const rowMin = Math.max( - 0, - Math.floor((pos[2] - r - this._gridOriginZ) / cs) - ); - const rowMax = Math.min( - numRows - 1, - Math.floor((pos[2] + r - this._gridOriginZ) / cs) - ); - - for (let col = colMin; col <= colMax; col++) { - for (let row = rowMin; row <= rowMax; row++) { - const cell = this._gridLookup[col * numCols + row]; - if (cell && isPosInRadius(r, cell.position, pos)) result.add(cell); - } - } - return result; - } - - /** - * Returns all grid cells for a specific position and radius - */ - getGridCellsInRadius(position: Float32Array, radius: number): GridCell[] { - const result: GridCell[] = []; - if (this._gridLookup.length === 0) return result; - - const cs = this._gridCellSize; - const numCols = this._gridNumCols; - const numRows = this._gridNumRows; + private spawnGridObjects(client: Client) { + const position = client.character.state.position; + for (const gridCell of this._grid) { + if ( + !isPosInRadius(client.chunkRenderDistance, gridCell.position, position) + ) + continue; - const colMin = Math.max( - 0, - Math.floor((position[0] - radius - this._gridOriginX) / cs) - ); - const colMax = Math.min( - numCols - 1, - Math.floor((position[0] + radius - this._gridOriginX) / cs) - ); - const rowMin = Math.max( - 0, - Math.floor((position[2] - radius - this._gridOriginZ) / cs) - ); - const rowMax = Math.min( - numRows - 1, - Math.floor((position[2] + radius - this._gridOriginZ) / cs) - ); + for (const object of gridCell.objects) { + if ( + client.spawnedEntities.has(object) || + !isPosInRadius( + (object.npcRenderDistance as number) || + this.charactersRenderDistance, + position, + object.state.position + ) + ) { + continue; + } - // Use a Set to deduplicate cells that span multiple lookup slots (mixed grid) - const seen = new Set(); - for (let col = colMin; col <= colMax; col++) { - for (let row = rowMin; row <= rowMax; row++) { - const cell = this._gridLookup[col * numCols + row]; - if (cell && !seen.has(cell)) { - seen.add(cell); - result.push(cell); + if (object instanceof ConstructionParentEntity) { + this.constructionManager.spawnConstructionParent( + this, + client, + object + ); + continue; } - } - } - return result; - } - /** Spawns a single grid entity for a client if not already spawned and within range */ - private spawnEntityForClient(client: Client, object: BaseEntity): void { - const position = client.character.state.position; - if ( - !isPosInRadius( - (object.npcRenderDistance as number) || this.charactersRenderDistance, - position, - object.state.position - ) - ) - return; + if (object instanceof ConstructionChildEntity) { + if (this.constructionManager.shouldHideEntity(this, client, object)) + continue; + this.constructionManager.spawnSimpleConstruction( + this, + client, + object + ); + continue; + } - // Construction types must bypass the spawnedEntities early-exit because - // their child entities (doors, walls) are spawned recursively and may have - // been skipped on a previous pass (e.g. !isSynced). spawnConstructionParent - // and spawnSimpleConstruction each guard themselves internally. - if (object instanceof ConstructionParentEntity) { - this.constructionManager.spawnConstructionParent(this, client, object); - return; - } - if (object instanceof ConstructionChildEntity) { - if (this.constructionManager.shouldHideEntity(this, client, object)) - return; - this.constructionManager.spawnSimpleConstruction(this, client, object); - return; - } - if (object instanceof ConstructionDoor) { - if (this.constructionManager.shouldHideEntity(this, client, object)) - return; - this.constructionManager.spawnConstructionDoor(this, client, object); - return; - } - if (object instanceof LootableConstructionEntity) { - if (this.constructionManager.shouldHideEntity(this, client, object)) - return; - this.constructionManager.spawnLootableConstruction(this, client, object); - return; - } + if (object instanceof LootableConstructionEntity) { + if (this.constructionManager.shouldHideEntity(this, client, object)) + continue; + this.constructionManager.spawnLootableConstruction( + this, + client, + object + ); + continue; + } - if (client.spawnedEntities.has(object)) return; - if (object instanceof BaseSimpleNpc) { - if (object instanceof Crate) { - if (object.spawnTimestamp > Date.now()) return; - if (object.destroyed) object.destroyed = false; - } - client.spawnedEntities.add(object); - this.addSimpleNpc(client, object); - return; - } - // During the loading phase, skip full NPC structs — client can't process them yet. - // executeRoutine re-subscribes after loading to catch these. - if ( - client.isLoading && - object instanceof BaseLightweightCharacter && - !object.useSimpleStruct - ) { - return; - } - client.spawnedEntities.add(object); - if (object instanceof BaseLightweightCharacter) { - if (object.useSimpleStruct) { - this.addSimpleNpc(client, object); - } else { - this.addLightweightNpc(client, object); - if (object instanceof DoorEntity) { - if (object.isOpen) { - this.sendData( - client, - "PlayerUpdatePosition", - { - transientId: object.transientId, - positionUpdate: { - sequenceTime: 0, - unknown3_int8: 0, - position: object.state.position, - orientation: object.openAngle - } - } - ); + if (object instanceof BaseSimpleNpc) { + if (object instanceof Crate) { + if (object.spawnTimestamp > Date.now()) { + continue; + } + if (object.destroyed) { + object.destroyed = false; + } } - return; - } - if (object instanceof Npc) { - object.updateEquipment(this); - return; + client.spawnedEntities.add(object); + this.addSimpleNpc(client, object); + continue; } - } - } - } - /** Spawns all objects in a cell for a client, yielding every N entities so - * ACKs can flow back before the SOE output window fills up. - * Batch size shrinks for high-ping clients to reduce the flood window. */ - private async spawnCellObjectsForClient( - client: Client, - cell: GridCell - ): Promise { - const ping = client.avgPing; - const batchSize = ping > 300 ? 10 : ping > 100 ? 25 : 50; - let i = 0; - for (const object of cell.objects) { - this.spawnEntityForClient(client, object); - if (++i % batchSize === 0) { - await new Promise((resolve) => setImmediate(resolve)); + client.spawnedEntities.add(object); + if (object instanceof BaseLightweightCharacter) { + if (object.useSimpleStruct) { + this.addSimpleNpc(client, object); + } else { + this.addLightweightNpc(client, object); + if (object instanceof DoorEntity) { + if (object.isOpen) { + this.sendData( + client, + "PlayerUpdatePosition", + { + transientId: object.transientId, + positionUpdate: { + sequenceTime: 0, + unknown3_int8: 0, + position: object.state.position, + orientation: object.openAngle + } + } + ); + } + continue; + } + if (object instanceof Npc) { + object.updateEquipment(this); + continue; + } + } + } } } } - /** Removes all spawned objects belonging to a cell from the client (called on cell unsubscription) */ - private despawnCellObjectsForClient(client: Client, cell: GridCell): void { - for (const object of cell.objects) { + private spawnLoadingGridObjects(client: Client) { + const position = client.character.state.position; + for (const gridCell of this._grid) { if ( - client.spawnedEntities.has(object) && - !(object instanceof Vehicle2016) + !isPosInRadius(client.chunkRenderDistance, gridCell.position, position) ) { - this.sendData(client, "Character.RemovePlayer", { - characterId: object.characterId - }); - client.spawnedEntities.delete(object); + continue; } - } - } - - /** Diffs old vs new visible cells and subscribes/unsubscribes the client accordingly. - * Caller is responsible for gating this on movement threshold. - * Async: yields the event loop between each newly-subscribed cell so that - * simultaneous logins interleave their entity-spawn work instead of stacking. */ - private async updateClientSubscriptions(client: Client): Promise { - const newCells = this.getVisibleCells(client); - const oldCells = client.subscribedCells; + for (const object of gridCell.objects) { + if ( + !isPosInRadius( + (object.npcRenderDistance as number) || + this.charactersRenderDistance, + position, + object.state.position + ) + ) { + continue; + } - // Update tracking state up-front so concurrent calls (login + first movement) - // see consistent state and won't re-process the same cells. - client.subscribedCells = newCells; - client.posAtLastCellUpdate = - client.character.state.position.slice() as Float32Array; - client.lastKnownChunkRenderDistance = client.chunkRenderDistance; + if (client.spawnedEntities.has(object)) continue; + if (object instanceof LootableConstructionEntity) continue; + if (object instanceof ConstructionChildEntity && + (object.itemDefinitionId == Items.WORKBENCH || + object.itemDefinitionId == Items.WORKBENCH_WEAPON + ) + ) continue; - // Spawn new cells, yielding after each one so the event loop stays - // responsive when many clients subscribe simultaneously. - for (const cell of newCells) { - if (!oldCells.has(cell)) { - cell.subscribers.add(client); - await this.spawnCellObjectsForClient(client, cell); - } - } + if (object instanceof BaseSimpleNpc) { + if (object instanceof Crate) { + if (object.spawnTimestamp > Date.now()) { + continue; + } + if (object.destroyed) { + object.destroyed = false; + } + } + client.spawnedEntities.add(object); + this.addSimpleNpc(client, object); + continue; + } - for (const cell of oldCells) { - if (!newCells.has(cell)) { - cell.subscribers.delete(client); - this.despawnCellObjectsForClient(client, cell); + if ( + object instanceof BaseLightweightCharacter && + object.useSimpleStruct + ) { + client.spawnedEntities.add(object); + this.addSimpleNpc(client, object); + } } } } - /** Called after an entity is pushed into a grid cell — propagates to subscribers in range */ - private onEntityAddedToCell(entity: BaseEntity, cell: GridCell): void { - for (const subscriber of cell.subscribers) { - this.spawnEntityForClient(subscriber as Client, entity); - } - } - private checkPlayersPositionsChallenges() { for (const key in this._characters) { const character = this._characters[key]; @@ -5000,32 +4569,22 @@ export class ZoneServer2016 extends EventEmitter { ); } - //TODO: This is temporary until we fix the ReplicationData correctly for construction Objects. - if ( - !( - entity instanceof ConstructionDoor || - entity instanceof ConstructionParentEntity || - entity instanceof ConstructionChildEntity || - entity instanceof LootableConstructionEntity - ) - ) { - this.sendData( - client, - "Replication.CreateComponent", - { - transientId: entity.transientId, - sequenceNumber: client.sentInteractionCounter++, - componentName: "ClientNpcComponent", - propertyHash: ReplicationPropertyHash.ISWORLDITEM, - payload: { - bufferData: { - nameId: nameId, - componentName: "ClientNpcComponent" - } + this.sendData( + client, + "Replication.CreateComponent", + { + transientId: entity.transientId, + sequenceNumber: client.sentInteractionCounter++, + componentName: "ClientNpcComponent", + propertyHash: ReplicationPropertyHash.ISWORLDITEM, + payload: { + bufferData: { + nameId: nameId, + componentName: "ClientNpcComponent" } } - ); - } + } + ); this.sendData( client, @@ -5309,11 +4868,8 @@ export class ZoneServer2016 extends EventEmitter { banReason: reason ? reason : "no reason", loginSessionId: loginSessionId, IP: - ( - await this._gatewayServer.getSoeClientNetworkInfos( - client?.soeClientId ?? "" - ) - )?.address ?? "", + this._gatewayServer.getSoeClientNetworkInfos(client?.soeClientId ?? "") + ?.address ?? "", adminId: adminId ? adminId : "", expirationDate: timestamp, active: true, @@ -5461,58 +5017,80 @@ export class ZoneServer2016 extends EventEmitter { //#region ********************VEHICLE******************** vehicleManager(client: Client) { - const pos = client.character.state.position; - const renderDist = this.charactersRenderDistance; - - // Spawn any in-range vehicles not yet visible to this client. - // Iterates this._vehicles directly (not the spatial map snapshot) so - // vehicles added after the last world-tick rebuild are not missed. for (const key in this._vehicles) { const vehicle = this._vehicles[key]; if ( - !isPosInRadius( - vehicle.npcRenderDistance || renderDist, - pos, + // vehicle spawning / managed object assignment logic + isPosInRadius( + vehicle.npcRenderDistance || this.charactersRenderDistance, + client.character.state.position, vehicle.state.position ) - ) - continue; - if (client.spawnedEntities.has(vehicle)) continue; - this.sendData(client, "AddLightweightVehicle", { - ...vehicle.pGetLightweightVehicle(), - unknownGuid1: this.generateGuid() - }); - vehicle.effectTags.forEach((effectTag: number) => { - this.sendData( - client, - "Character.AddEffectTagCompositeEffect", - { - characterId: vehicle.characterId, - effectId: effectTag, - unknownDword1: effectTag, - unknownDword2: effectTag + ) { + if (!client.spawnedEntities.has(vehicle)) { + this.sendData( + client, + "AddLightweightVehicle", + { + ...vehicle.pGetLightweightVehicle(), + unknownGuid1: this.generateGuid() + } + ); + vehicle.effectTags.forEach((effectTag: number) => { + this.sendData( + client, + "Character.AddEffectTagCompositeEffect", + { + characterId: vehicle.characterId, + effectId: effectTag, + unknownDword1: effectTag, + unknownDword2: effectTag + } + ); + }); + /* + if (vehicle.engineOn) { + this.sendData<>(client, "Vehicle.Engine", { + vehicleCharacterId: vehicle.characterId, + engineOn: true, + }); } - ); - }); - client.spawnedEntities.add(vehicle); - } - - // Despawn vehicles that are no longer in range. - for (const entity of client.spawnedEntities) { - if (!(entity instanceof Vehicle)) continue; - if ( - isPosInRadius( - entity.npcRenderDistance || renderDist, - pos, - entity.state.position + */ + /*this.sendData<>(client, "Vehicle.OwnerPassengerList", { + characterId: client.character.characterId, + passengers: vehicle.pGetPassengers(this), + });*/ + client.spawnedEntities.add(vehicle); + } + // disable managing vehicles with routine, leaving only managed when entering it + /*if (!vehicle.isManaged) { + // assigns management to first client within radius + this.assignManagedObject(client, vehicle); + }*/ + } else if ( + !isPosInRadius( + this.charactersRenderDistance, + client.character.state.position, + vehicle.state.position ) - ) - continue; - if (entity.isManaged) this.dropManagedObject(client, entity); - this.sendData(client, "Character.RemovePlayer", { - characterId: entity.characterId - }); - client.spawnedEntities.delete(entity); + ) { + // vehicle despawning / managed object drop logic + + const vehicleExist = client.spawnedEntities.has(vehicle); + if (vehicleExist) { + if (vehicle.isManaged) { + this.dropManagedObject(client, vehicle); + } + this.sendData( + client, + "Character.RemovePlayer", + { + characterId: vehicle.characterId + } + ); + client.spawnedEntities.delete(vehicle); + } + } } } @@ -5687,7 +5265,7 @@ export class ZoneServer2016 extends EventEmitter { } sendDataToAllWithSpawnedEntity( - _dictionary: EntityDictionary, + dictionary: EntityDictionary, entityCharacterId: string = "", packetName: h1z1PacketsType2016, obj: ZonePacket @@ -5696,16 +5274,14 @@ export class ZoneServer2016 extends EventEmitter { const data = this._protocol.pack(packetName, obj); if (!data) return; this.debugSendData(packetName); - // Send to all clients who have this entity spawned. - const observers = this._entityObservers.get(entityCharacterId); - if (observers) { - for (const client of observers) { - this.sendRawDataReliable(client, data); + for (const a in this._clients) { + if ( + this._clients[a].spawnedEntities.has(dictionary[entityCharacterId]) || + this._clients[a].character.characterId == entityCharacterId + ) { + this.sendRawDataReliable(this._clients[a], data); } } - // Also send to the entity's own client (they don't spawn themselves). - const ownerClient = this.getClientByCharId(entityCharacterId); - if (ownerClient) this.sendRawDataReliable(ownerClient, data); } sendDataToAllInRange( @@ -5717,21 +5293,21 @@ export class ZoneServer2016 extends EventEmitter { const data = this._protocol.pack(packetName, obj); if (!data) return; this.debugSendData(packetName); - const [cx0, cx1, cz0, cz1] = ZoneServer2016._charGridRange(position, range); - for (let cx = cx0; cx <= cx1; cx++) { - for (let cz = cz0; cz <= cz1; cz++) { - const bucket = this._charSpatialMap.get(`${cx},${cz}`); - if (!bucket) continue; - for (const client of bucket) { - if (isPosInRadius(range, client.character.state.position, position)) - this.sendRawDataReliable(client, data); - } + for (const a in this._clients) { + if ( + isPosInRadius( + range, + this._clients[a].character.state.position, + position + ) + ) { + this.sendRawDataReliable(this._clients[a], data); } } } sendDataToAllOthersWithSpawnedEntity( - _dictionary: EntityDictionary, + dictionary: EntityDictionary, client: Client, entityCharacterId: string = "", packetName: h1z1PacketsType2016, @@ -5741,10 +5317,13 @@ export class ZoneServer2016 extends EventEmitter { const data = this._protocol.pack(packetName, obj); if (!data) return; this.debugSendData(packetName); - const observers = this._entityObservers.get(entityCharacterId); - if (!observers) return; - for (const c of observers) { - if (c !== client) this.sendRawDataReliable(c, data); + for (const a in this._clients) { + if ( + client != this._clients[a] && + this._clients[a].spawnedEntities.has(dictionary[entityCharacterId]) + ) { + this.sendRawDataReliable(this._clients[a], data); + } } } @@ -5761,16 +5340,10 @@ export class ZoneServer2016 extends EventEmitter { getClientsInRange(range: number, position: Float32Array): Client[] { const clients: Client[] = []; - const [cx0, cx1, cz0, cz1] = ZoneServer2016._charGridRange(position, range); - for (let cx = cx0; cx <= cx1; cx++) { - for (let cz = cz0; cz <= cz1; cz++) { - const bucket = this._charSpatialMap.get(`${cx},${cz}`); - if (!bucket) continue; - for (const client of bucket) { - if (isPosInRadius(range, client.character.state.position, position)) - clients.push(client); - } - } + for (const a in this._clients) { + const client = this._clients[a]; + if (isPosInRadius(range, client.character.state.position, position)) + clients.push(client); } return clients; } @@ -7766,7 +7339,6 @@ export class ZoneServer2016 extends EventEmitter { plant.isFertilized = true; const roz = (plant.nextStateTime - new Date().getTime()) / 2; plant.nextStateTime = new Date().getTime() + roz; - plant.nextMoundTime = new Date().getTime() + 180000; this.sendDataToAllWithSpawnedEntity( // play burning effect & remove it after 15s this._plants, @@ -9274,19 +8846,6 @@ export class ZoneServer2016 extends EventEmitter { } } - detectSnaking( - server: ZoneServer2016, - client: Client, - stanceFlags: StanceFlags - ) { - if (stanceFlags.SITTING) { - server.multiplyMovementModifier(client, 0.2); - setTimeout(() => { - server.divideMovementModifier(client, 0.2); - }, 2000); - } - } - //#endregion async reloadZonePacketHandlers() { @@ -9379,151 +8938,82 @@ export class ZoneServer2016 extends EventEmitter { } } } - executeRoutine(client: Client) { - // Reset subscriptions so updateClientSubscriptions re-populates everything, - // including full NPC structs that were skipped during the loading phase. - for (const cell of client.subscribedCells) { - cell.subscribers.delete(client); + async startRoutinesLoop() { + if (_.size(this._clients) <= 0) { + this.routinesLoopTimer = setTimeout(() => { + this.startRoutinesLoop(); + }, 3000); + return; + } + for (const a in this._clients) { + while (this.isSaving) { + await scheduler.wait(500); + } + const startTime = Date.now(); + const client = this._clients[a]; + if (!client.isLoading) { + client.routineCounter++; + this.constructionManager.constructionPermissionsManager(this, client); + if (!this.disableMapBoundsCheck) { + this.checkInMapBounds(client); + } + this.checkZonePing(client); + if (client.routineCounter >= 3) { + this.createFairPlayInternalPacket(client); + this.assignChunkRenderDistance(client); + this.removeOutOfDistanceEntities(client); + if (!this.disablePOIManager) { + this.POIManager(client); + } + client.routineCounter = 0; + } + //this.constructionManager.spawnConstructionParentsInRange(this, client); // put back into grid for now + this.vehicleManager(client); + this.spawnGridObjects(client); // Spawn base parts before the player + this.spawnCharacters(client); + //this.constructionManager.worldConstructionManager(this, client); // put into grid + client.posAtLastRoutine = client.character.state.position; + } + const endTime = Date.now(); + const timeTaken = endTime - startTime; + if (timeTaken > this.tickRate) { + console.log( + `Routine took ${timeTaken}ms to execute, which is more than the tickRate ${this.tickRate}` + ); + } + await scheduler.wait(this.tickRate, {}); } - client.subscribedCells.clear(); - client.posAtLastCellUpdate = new Float32Array([0, 0, 0, 1]); + this.updateSpectatorMap(); + this.startRoutinesLoop(); + } + + executeRoutine(client: Client) { this.constructionManager.constructionPermissionsManager(this, client); - client.posAtLastPermissionCheck = - client.character.state.position.slice() as Float32Array; //this.constructionManager.spawnConstructionParentsInRange(this, client); // put into grid this.vehicleManager(client); this.removeOutOfDistanceEntities(client); - void this.updateClientSubscriptions(client); + this.spawnGridObjects(client); // Spawn base parts before the player this.spawnCharacters(client); //this.constructionManager.worldConstructionManager(this, client); if (!this.disablePOIManager) { this.POIManager(client); } - client.posAtLastRoutine = - client.character.state.position.slice() as Float32Array; - client.lastRoutineTime = Date.now(); - this.startClientPeriodicChecks(client); + client.posAtLastRoutine = client.character.state.position; } firstRoutine(client: Client) { //this.constructionManager.spawnConstructionParentsInRange(this, client); // put into grid - void this.updateClientSubscriptions(client); + this.spawnLoadingGridObjects(client); this.spawnCharacters(client); //this.constructionManager.worldConstructionManager(this, client); if (!this.disablePOIManager) { this.POIManager(client); } - client.posAtLastRoutine = - client.character.state.position.slice() as Float32Array; - } - - /** Runs the world-state update for a single client based on their current - * known position. Called from the server tick — NOT from packet handlers. */ - private runClientTick(client: Client) { - if (client.isLoading) return; - const pos = client.character.state.position; - - // Construction permissions: run when player has moved 3+ units - if (getDistance2d(pos, client.posAtLastPermissionCheck) >= 3) { - this.constructionManager.constructionPermissionsManager(this, client); - client.posAtLastPermissionCheck = pos.slice() as Float32Array; - } - - // Heavy world scans: run when player has moved 10+ units - if (getDistance2d(pos, client.posAtLastRoutine) >= 10) { - if (!this.disableMapBoundsCheck) this.checkInMapBounds(client); - this.assignChunkRenderDistance(client); - if (!this.disablePOIManager) this.POIManager(client); - this.vehicleManager(client); - - // Spawn any entities in already-subscribed cells that weren't in render - // range when first subscribed — e.g. NPCs that spawned nearby. - for (const cell of client.subscribedCells) { - this.spawnCellObjectsForClient(client, cell); - } - - client.posAtLastRoutine = pos.slice() as Float32Array; - client.lastRoutineTime = Date.now(); - } - - // Cell subscriptions: run when player has crossed 62.5 units or render distance changed - if ( - getDistance2d(pos, client.posAtLastCellUpdate) >= 62.5 || - client.chunkRenderDistance !== client.lastKnownChunkRenderDistance - ) { - void this.updateClientSubscriptions(client); - } - - this.spawnCharacters(client); - } - - private _worldTickTimer?: NodeJS.Timeout; - private static readonly WORLD_TICK_MS = 200; - - private _rebuildSpatialMaps() { - const sz = ZoneServer2016._CHAR_GRID_SIZE; - - this._charSpatialMap.clear(); - for (const sessionId in this._clients) { - const c = this._clients[sessionId]; - if (!c.character.isReady || c.isLoading) continue; - const pos = c.character.state.position; - const key = `${Math.floor(pos[0] / sz)},${Math.floor(pos[2] / sz)}`; - let bucket = this._charSpatialMap.get(key); - if (!bucket) { - bucket = []; - this._charSpatialMap.set(key, bucket); - } - bucket.push(c); - } - } - - startWorldTick() { - const tick = () => { - if (!this._ready) { - this._worldTickTimer = setTimeout(tick, ZoneServer2016.WORLD_TICK_MS); - return; - } - this._rebuildSpatialMaps(); - for (const sessionId in this._clients) { - this.runClientTick(this._clients[sessionId]); - } - this._worldTickTimer = setTimeout(tick, ZoneServer2016.WORLD_TICK_MS); - }; - this._worldTickTimer = setTimeout(tick, ZoneServer2016.WORLD_TICK_MS); - } - - startClientPeriodicChecks(client: Client) { - clearTimeout(client.pingTimer); - clearTimeout(client.fairPlayTimer); - - const pingLoop = () => { - if (!this._clients[client.sessionId]) return; - if (!client.isLoading) this.checkZonePing(client); - client.pingTimer = setTimeout(pingLoop, 3000); - }; - client.pingTimer = setTimeout(pingLoop, 3000); - - const fairPlayLoop = () => { - if (!this._clients[client.sessionId]) return; - if (!client.isLoading) { - this.createFairPlayInternalPacket(client); - // Clean up out-of-distance entities, then re-spawn anything in subscribed - // cells that has come back into render range since the last cleanup. - this.removeOutOfDistanceEntities(client); - for (const cell of client.subscribedCells) { - this.spawnCellObjectsForClient(client, cell); - } - } - client.fairPlayTimer = setTimeout(fairPlayLoop, 9000); - }; - client.fairPlayTimer = setTimeout(fairPlayLoop, 9000); + client.posAtLastRoutine = client.character.state.position; } async checkZonePing(client: Client) { - const ping = await this._gatewayServer.getSoeClientAvgPing( - client.soeClientId - ); + const ping = this._gatewayServer.getSoeClientAvgPing(client.soeClientId); if ( client.isAdmin || Number(client.character.lastLoginDate) + 30000 > new Date().getTime() || @@ -9540,8 +9030,7 @@ export class ZoneServer2016 extends EventEmitter { ); client.pingWarnings += 1; } else { - // Decay rather than hard-reset — one good sample shouldn't erase accumulated warnings - client.pingWarnings = Math.max(0, client.pingWarnings - 1); + client.pingWarnings = 0; } if (client.zonePings.length < 15) return; @@ -9549,7 +9038,7 @@ export class ZoneServer2016 extends EventEmitter { client.zonePings.reduce((a, b) => a + b, 0) / client.zonePings.length; if ( averagePing >= this.fairPlayManager.maxPing && - client.pingWarnings > 6 + client.pingWarnings > 3 ) { this.kickPlayer(client); this.sendChatTextToAdmins( @@ -9741,26 +9230,6 @@ export class ZoneServer2016 extends EventEmitter { } } - private initializePacketEncodingWorker() { - this.packetEncodingWorker = new PacketEncodingWorker(this._clientProtocol); - } - - private async packPacket( - packetName: h1z1PacketsType2016, - obj: ZonePacket - ): Promise { - if (this.packetEncodingWorker) { - try { - return await this.packetEncodingWorker.encodePacket(packetName, obj); - } catch (error) { - debug( - `Packet encoding worker failed for ${packetName}, falling back to local pack: ${error}` - ); - } - } - return this._protocol.pack(packetName, obj); - } - sendDataToAll(packetName: h1z1PacketsType2016, obj: ZonePacket) { this._sendDataToAll(packetName, obj); } @@ -9788,74 +9257,6 @@ export class ZoneServer2016 extends EventEmitter { ); } - sendLoginServerMessage(data: any) { - this._loginConnectionManager.sendData( - { - ...this._loginServerInfo, - serverId: this._worldId - } as any, - "ClientMessage", - data - ); - } - - executeGlobalBroadcast( - broadcastType: number, - initiatorName: string, - message: string, - rewardIds: { rewardId: number }[] - ) { - const identity = { characterFirstName: initiatorName }; - switch (broadcastType) { - case 0: // global announcement - this._sendDataToAll("Broadcast.World", { identity, message }); - break; - case 1: // global reward drop to all players - for (const key in this._clients) { - const c = this._clients[key]; - for (const { rewardId } of rewardIds) { - this.rewardManager.addRewardToPlayer(c, rewardId); - } - } - if (message) - this._sendDataToAll("Broadcast.World", { identity, message }); - break; - default: - debug(`Unknown GlobalBroadcast type: ${broadcastType}`); - break; - } - } - - sendGlobalBroadcastRequest( - broadcastType: number, - initiatorName: string, - message: string, - rewardIds: number[] = [] - ) { - if (this._soloMode) { - this.executeGlobalBroadcast( - broadcastType, - initiatorName, - message, - rewardIds.map((rewardId) => ({ rewardId })) - ); - return; - } - this._loginConnectionManager.sendData( - { - ...this._loginServerInfo, - serverId: this._worldId - } as any, - "GlobalBroadcastRequest", - { - broadcastType, - initiatorName, - message, - rewardIds: rewardIds.map((rewardId) => ({ rewardId })) - } - ); - } - private filterOutOfDistance( element: BaseEntity, playerPosition: Float32Array @@ -10121,34 +9522,13 @@ export class ZoneServer2016 extends EventEmitter { isBattleRoyale(): boolean { return this.gameMode > 0; } - - isPosInPoi(position: Float32Array): boolean { - let isInPoi = false; - Z1_POIs.forEach((point: any) => { - let useRange = true; - if (point.bounds) { - useRange = false; - point.bounds.forEach((bound: any) => { - if (isInsideSquare([position[0], position[2]], bound)) { - isInPoi = true; - return; - } - }); - } - if (useRange && isPosInRadius(point.range, position, point.position)) { - isInPoi = true; - } - }); - - return isInPoi; - } } if (process.env.VSCODE_DEBUG === "true") { const PackageSetting = require("../../../package.json"); process.env.H1Z1_SERVER_VERSION = PackageSetting.version; new ZoneServer2016( - Number(process.env.SERVER_BIND_PORT) || 1117, + 1117, Buffer.from(DEFAULT_CRYPTO_KEY, "base64"), process.env.MONGO_URL, 2 diff --git a/src/types/LoginUdp_11packets.ts b/src/types/LoginUdp_11packets.ts index 28f5efe7db..b0d280a3cf 100644 --- a/src/types/LoginUdp_11packets.ts +++ b/src/types/LoginUdp_11packets.ts @@ -38,7 +38,7 @@ export interface LoginReply { unknownString6?: string; unknownString7?: string; }; - errorDetails: unknown[]; + unknownArray2: unknown[]; unknownString2?: string; unknownString3?: string; unknownBoolean3?: boolean; diff --git a/src/types/zone2016packets.ts b/src/types/zone2016packets.ts index a0600cbb4d..e35c6955b4 100644 --- a/src/types/zone2016packets.ts +++ b/src/types/zone2016packets.ts @@ -516,16 +516,6 @@ export interface PlayerStop { transientId: unknown; state?: boolean; } -export interface PlayerUpdateAttachObject { - gameTick?: number; - sourceCharacter?: string; - targetCharacter?: string; - unknownFloatVector1?: Float32Array; - unknownFloatVector2?: Float32Array; - unknownDword2?: number; - unknownShort1?: number; - unknownDword3?: number; -} export interface ClientSettings { helpUrl?: string; shopUrl?: string; @@ -2485,7 +2475,7 @@ export interface VehicleOccupy { } export interface VehicleStateData { guid?: string; - gameTick?: number; + unknownDword1?: number; unknownArray1?: unknown[]; unknownArray2?: unknown[]; } @@ -4326,4 +4316,4 @@ export interface ShaderParameterOverrideBase { unknownDword2?: number; shaderGroupId?: number; } -export type zone2016packets = ClientFinishedLoading | SendSelfToClient | ClientIsReady | ZoneDoneSendingInitialData | ClientBeginZoning | ProjectileDebug | SendZoneDetails | GameTimeSync | UpdateClientSessionData | WorldDisplayInfo | SetLocale | WorldShutdownNotice | KeepAlive | ClientExitLaunchUrl | MembershipActivation | ShowSystemMessage | POIChangeMessage | ClientLog | CommerceSessionResponse | LoginFailed | NpcCollision | ClientGameSettings | ClientTrialProfileUpsell | PlayerTitle | UpdateUserAge | InitializationParameters | ClientInitializationDetails | ClientFlashTimer | PlayerUpdatePosition | Synchronization | PlayerUpdateManagedPosition | AddSimpleNpc | ContinentBattleInfo | GetContinentBattleInfo | SendSecurityPacketAndSelfDestruct | GetRespawnLocations | Security | ServerPopulationInfo | GetServerPopulationInfo | VehicleCollision | PlayerStop | PlayerUpdateAttachObject | ClientSettings | RewardBuffInfo | GetRewardBuffInfo | CharacterSelectSessionResponse | UpdateWeatherData | AddLightweightPc | AddLightweightNpc | AddLightweightVehicle | AddProxiedObject | LightweightToFullPc | LightweightToFullNpc | LightweightToFullVehicle | FairPlayInternal | CharacterRemovePlayer | CharacterKnockback | CharacterUpdateHitpoints | CharacterPlayAnimation | CharacterUpdateScale | CharacterUpdateTemporaryAppearance | CharacterSetLookAt | CharacterRenamePlayer | CharacterUpdateCharacterState | CharacterExpectedSpeed | CharacterThoughtBubble | CharacterSlotCompositeEffectOverride | CharacterPreferredLanguages | CharacterCustomizationChange | CharacterPlayerTitle | CharacterAddEffectTagCompositeEffect | CharacterRemoveEffectTagCompositeEffect | CharacterCustomizeNpc | CharacterSetSpawnerActivationEffect | CharacterSetComboState | CharacterSetSurpriseState | CharacterRemoveNpcCustomization | CharacterReplaceBaseModel | CharacterSetCollidable | CharacterUpdateOwner | CharacterWeaponStance | CharacterUpdateTintAlias | CharacterMoveOnRail | CharacterClearMovementRail | CharacterMoveOnRelativeRail | CharacterDestroyed | CharacterSeekTarget | CharacterSeekTargetUpdate | CharacterUpdateActiveWieldType | CharacterMemberStatus | CharacterKnockedOut | CharacterKnockedOutDamageReport | CharacterRespawn | CharacterRespawnReply | CharacterActivateProfile | CharacterJet | CharacterStartRevive | CharacterStopRevive | CharacterSetFaction | CharacterSetBattleRank | CharacterStartHeal | CharacterStopHeal | CharacterManagedObject | CharacterMaterialTypeOverride | CharacterDebrisLaunch | CharacterHideCorpse | CharacterCharacterStateDelta | CharacterUpdateStat | CharacterAnimationRequest | CharacterPlayWorldCompositeEffect | CharacterAFK | CharacterFullCharacterDataRequest | CharacterDeploy | CharacterKilledBy | CharacterMotorRunning | CharacterDroppedItemNotification | CharacterNoSpaceNotification | CharacterStartMultiStateDeath | CharacterAggroLevel | CharacterDoorState | CharacterSetAllowRespawn | CharacterUpdateGuildTag | CharacterMovementVersion | CharacterDailyRepairMaterials | CharacterUpdateTwitchInfo | CharacterUpdateSimpleProxyHealth | GroupInvite | GroupJoin | GroupAutoGroup | GroupLeave | GroupKick | GroupDisband | GroupSetGroupFlags | GroupSetGroupOwner | GroupSetGroupDescription | GroupUnknownA | GroupMapPingRelated | GroupUnknownC | GroupGetGroup | GroupUnknownF | GroupJoinLookingForMore | GroupToggleSquadLeaderChat | GroupUnknown12 | GroupPlayerJoined | GroupUnknown14 | GroupRemoveGroup | GroupRemoveMember | GroupRemoveInvitation | GroupUnknown19 | GroupUnknown1a | GroupRaidCreate | ReferenceDataItemClassDefinitions | ReferenceDataItemCategoryDefinitions | ReferenceDataProfileDefinitions | ReferenceDataWeaponDefinitions | ReferenceDataProjectileDefinitions | ReferenceDataDynamicAppearance | UiTaskAdd | UiTaskUpdate | UiTaskComplete | UiTaskFail | UiExecuteScript | UiStartTimer | UiObjectiveTargetUpdate | UiMessage | UiCinematicStartLookAt | UiWeaponHitFeedback | UiHeadShotFeedback | UiWaypointCooldown | UiZoneWaypoint | UiWaypointNotify | UiInteractStart | UiRewardNotification | UiWarpgateRotateWarning | UiConfirmHit | RewardAddRewardItem | RewardAddNonRewardItem | RecipeAdd | RecipeComponentUpdate | RecipeRemove | RecipeDiscovery | RecipeDiscoveries | RecipeRequestDiscovery | RecipeUnk8 | RecipeList | InGamePurchaseServerStatusResponse | FriendList | FriendMessage | ClientPathRequest | ClientPathReply | LobbyJoinLobbyGame | LobbyLeaveLobbyGame | LobbyStartLobbyGame | LobbyUpdateLobbyGame | LobbySendLobbyToClient | LobbyLobbyErrorMessage | LobbyShowLobbyUi | FirstTimeEventNotifySystem | FirstTimeEventState | FirstTimeEventUnknown2 | FirstTimeEventUnknown3 | FirstTimeEventScript | FirstTimeEventUnknown4 | GuildDisband | GuildRename | GuildChangeMemberRank | GuildMotdUpdate | AchievementAdd | AchievementInitialize | Loot | MountMountResponse | MountDismountRequest | MountDismountResponse | MountList | MountOfferUpsell | MountSeatChangeRequest | MountSeatChangeResponse | MountSeatSwapRequest | MountFlipMount | TargetUnk7 | TargetUnk8 | TargetUnk9 | TargetUnk10 | TargetUnk11 | TargetUnk12 | Ping | Pong | VoiceLogin | VoiceJoinChannel | VoiceLeaveChannel | VoiceRadioChannel | VoiceLeaveRadio | VoiceUnk9 | WeaponWeapon | FacilityReferenceData | FacilityFacilityData | FacilitySpawnCollisionChanged | SkillSetSkillPointManager | SkillSetSkillPointProgress | LoadoutSelectLoadout | LoadoutUnk1 | LoadoutSetLoadoutSlots | LoadoutSetLoadoutSlot | LoadoutSelectSlot | LoadoutCreateCustomLoadout | ExperienceSetExperienceRanks | ExperienceSetExperienceRateTier | VehicleOwner | VehicleOccupy | VehicleStateData | VehicleStateDamage | VehicleSpawn | VehicleTint | VehicleActiveWeapon | VehicleStats | VehicleDamageInfo | VehicleStatUpdate | VehicleUpdateWeapon | VehicleUpdateQueuePosition | VehicleSetAutoDrive | VehicleLockOnInfo | VehicleLockOnState | VehicleTrackingState | VehicleCounterMeasureState | VehicleLoadVehicleDefinitionManager | VehicleAcquireState | VehicleAutoMount | VehicleDeploy | VehicleEngine | VehicleAccessType | VehicleHealthUpdateOwner | VehicleOwnerPassengerList | VehicleKick | VehicleNoAccess | VehicleExpiration | VehicleGroup | VehicleDeployResponse | VehicleControllerLogOut | VehicleCurrentMoveMode | VehicleItemDefinitionRequest | VehicleItemDefinitionReply | VehicleInventoryItems | ResourceEvent | CollisionDamage | EquipmentSetCharacterEquipment | EquipmentSetCharacterEquipmentSlot | EquipmentUnsetCharacterEquipmentSlot | EquipmentSetCharacterEquipmentSlots | DefinitionFilterSetDefinitionVariable | DefinitionFilterSetDefinitionIntSet | DefinitionFilterUnknownWithVariable1 | DefinitionFilterUnknownWithVariable2 | H1emuPrintToConsole | H1emuMessageBox | H1emuRequestAssetHashes | H1emuVoiceInit | H1emuRequestModules | H1emuRequestWindows | H1emuVoiceState | WallOfDataUIEvent | WallOfDataClientSystemInfo | WallOfDataClientTransition | EffectAddEffect | EffectUpdateEffect | EffectRemoveEffect | EffectAddEffectTag | EffectRemoveUiIndicators | EffectAddUiIndicator | AbilitiesInitAbility | AbilitiesUpdateAbility | AbilitiesUninitAbility | AbilitiesSetActivatableAbilityManager | AbilitiesSetVehicleActivatableAbilityManager | AbilitiesActivateAbility | AbilitiesDeactivateAbility | AbilitiesVehicleDeactivateAbility | AbilitiesActivateAbilityFailed | AbilitiesClearAbilityLineManager | AbilitiesSetProfileAbilityLineMembers | AbilitiesSetLoadoutAbilities | AbilitiesAddLoadoutAbility | AbilitiesAddPersistentAbility | AbilitiesSetProfileRankAbilities | MapRegionGlobalData | MapRegionData | MapRegionMapOutOfBounds | MapRegionRequestContinentData | AcquireTimers | ItemsSetItemTimerManager | ItemsSetItemTrialLockTimer | ItemsSetAccountItemManager | ItemsAddAccountItem | ItemsRemoveAccountItem | ItemsUpdateAccountItem | ItemsSetEscrowAccountItemManager | ItemsAddEscrowAccountItem | ItemsRemoveEscrowAccountItem | ItemsUpdateEscrowAccountItem | ItemsAccountItemManagerStateChanged | ItemsReportNewRewardCrateAdded | ItemsReportRewardCrateContents | ItemsSetEmoteItem | ItemsRemoveEmoteItem | ItemsSetSkinItemManager | ItemsSetSkinItem | ItemsSetCurrentSkinItemCollection | ItemsRequestUseItem | ItemsRequestUseAccountItem | CurrencySetCurrencyDiscount | ZoneSettingData | WordFilterData | StaticFacilityInfoAllZones | OperationClientClearMissions | WordFilterData | StatsUnk2 | StatsUnk3 | StatsUnk4 | StatsAllPlayerStatLeaderboard | StatsPlayersLeaderboard | LocksShowMenu | CharacterStateTimerDataSource | CharacterStateInteractionStart | CharacterStateInteractionStop | CharacterStateUpdateTimerDataSource | AudioSetState | AudioSetSwitch | AudioPostEvent | NpcFoundationPermissionsManagerAddPermission | NpcFoundationPermissionsManagerEditPermission | NpcFoundationPermissionsManagerBaseShowPermissions | ReplicationCreateRepData | ReplicationUpdateRepData | ReplicationCreateComponent | VehicleSkinSetVehicleSkinManager | AnimationRequest | AnimationPlay | ChatChat | ChatEnterArea | ChatDebugChat | ChatFromStringId | ChatChatText | CommandPlaySoundAtLocation | CommandInteractRequest | CommandInteractCancel | CommandInteractDebug | CommandInteractionList | CommandInteractionSelect | CommandSetProfile | CommandPlayerSelect | CommandFreeInteractionNpc | CommandRecipeStart | CommandShowRecipeWindow | CommandPlayDialogEffect | CommandPlaySoundIdOnTarget | CommandInteractionString | CommandAddWorldCommand | CommandAddZoneCommand | CommandExecuteCommand | CommandZoneExecuteCommand | CommandItemDefinitionRequest | CommandItemDefinitionReply | CommandItemDefinitions | CommandEnableCompositeEffects | CommandRequestWeaponFireStateUpdate | CommandDeliveryDisplayInfo | CommandDeliveryManagerStatus | CommandDeliveryManagerShowNotification | CommandReportLastDeath | CommandPointAndReport | CommandSpawnVehicle | CommandRunSpeed | CommandAddItem | ClientUpdateItemAdd | ClientUpdateItemUpdate | ClientUpdateItemDelete | ClientUpdateUpdateStat | ClientUpdateUpdateLocation | ClientUpdateActivateProfile | ClientUpdateDoneSendingPreloadCharacters | ClientUpdateDamageInfo | ClientUpdateRespawnLocations | ClientUpdateModifyMovementSpeed | ClientUpdateModifyTurnRate | ClientUpdateModifyStrafeSpeed | ClientUpdateUpdateManagedLocation | ClientUpdateManagedMovementVersion | ClientUpdateUpdateWeaponAddClips | ClientUpdateStartTimer | ClientUpdateCompleteLogoutProcess | ClientUpdateProximateItems | ClientUpdateTextAlert | ClientUpdateNetworkProximityUpdatesComplete | ClientUpdateDeathMetrics | ClientUpdateManagedObjectResponseControl | ClientUpdateNpcRelevance | ClientUpdateMonitorTimeDrift | ClientUpdateUpdateRewardAndGrinderState | ClientUpdateUpdateLockoutTimes | ClientUpdateZoneStatus | InGamePurchasePreviewOrderResponse | InGamePurchasePlaceOrderResponse | InGamePurchaseStoreBundles | InGamePurchaseStoreBundleCategoryGroups | InGamePurchaseStoreBundleCategories | InGamePurchaseExclusivePartnerStoreBundles | InGamePurchaseStoreBundleGroups | InGamePurchaseWalletInfoResponse | InGamePurchaseStationCashProductsResponse | InGamePurchaseStateCodesResponse | InGamePurchaseCountryCodesResponse | InGamePurchaseSubscriptionProductsResponse | InGamePurchaseEnableMarketplace | InGamePurchaseAccountInfoRequest | InGamePurchaseAccountInfoResponse | InGamePurchaseStoreBundleContentRequest | InGamePurchaseStoreBundleContentResponse | InGamePurchaseClientStatistics | InGamePurchaseDisplayMannequinStoreBundles | InGamePurchaseItemOfTheDay | InGamePurchaseEnablePaymentSources | InGamePurchaseSetMembershipFreeItemInfo | InGamePurchaseGiftOrderNotification | InGamePurchaseActiveSchedules | InGamePurchaseNudgeOfferNotification | InGamePurchaseSpiceWebAuthUrlResponse | InGamePurchaseBundlePriceUpdate | InGamePurchaseWalletBalanceUpdate | InGamePurchaseMemberFreeItemCount | QuickChatSendData | BroadcastLocal | BroadcastZone | BroadcastWorld | LobbyGameDefinitionDefinitionsRequest | LobbyGameDefinitionDefinitionsResponse | CoinStoreItemList | CoinStoreSellToClientRequest | CoinStoreTransactionComplete | ProfileStatsGetPlayerProfileStats | H1emuFairPlay | H1emuHeartBeat | DtoHitReportPacket | DtoStateChange | DtoObjectInitialData | DtoHitSpeedTreeReport | ContainerMoveItem | ContainerInitEquippedContainers | ContainerError | ContainerListAll | ContainerUpdateEquippedContainer | ConstructionPlacementRequest | ConstructionPlacementResponse | ConstructionPlacementFinalizeRequest | ConstructionPlacementFinalizeResponse | ConstructionUnknown | LocksSetLock | RagdollStart | RagdollUpdatePose | RagdollUnk2 | RagdollUnk | RagdollStop | GameModeUpdateToxicGas | GameModeUpdateSafeZone | GameModeDeathInfo | GameModeStartLogout | GameModePlayersRemaining | GameModeTeamsRemaining | GameModeUnk11 | GameModeUnk13 | GameModeUnk15 | GameModeUnk16 | GameModeUnk18 | GameModeUnk19 | GameModeUnk20 | GameModeUnk21 | GameModeStartMatch | GameModeUnk23 | GameModeShowVictoryScreen | GrinderExchangeRequest | GrinderExchangeResponse | ScreenEffectApplyScreenEffect | ScreenEffectRemoveScreenEffect | SpectatorEnable | SpectatorAllSpectators | SpectatorUnknown3 | SpectatorTeleport | SpectatorDeathList | SpectatorSetModerator | SpectatorSetOwner | SpectatorMatchResults | SpectatorUnknown12 | SynchronizedTeleportWaitingForPlayers | SynchronizedTeleportNotifyReady | SynchronizedTeleportPlayersReady | SynchronizedTeleportRelease | AccessedCharacterBeginCharacterAccess | AccessedCharacterEndCharacterAccess | AccessedCharacterUpdateMutatorRights | AccessedCharacterUnknown3 | AccessedCharacterUnknown2 | ShaderParameterOverrideBase; \ No newline at end of file +export type zone2016packets = ClientFinishedLoading | SendSelfToClient | ClientIsReady | ZoneDoneSendingInitialData | ClientBeginZoning | ProjectileDebug | SendZoneDetails | GameTimeSync | UpdateClientSessionData | WorldDisplayInfo | SetLocale | WorldShutdownNotice | KeepAlive | ClientExitLaunchUrl | MembershipActivation | ShowSystemMessage | POIChangeMessage | ClientLog | CommerceSessionResponse | LoginFailed | NpcCollision | ClientGameSettings | ClientTrialProfileUpsell | PlayerTitle | UpdateUserAge | InitializationParameters | ClientInitializationDetails | ClientFlashTimer | PlayerUpdatePosition | Synchronization | PlayerUpdateManagedPosition | AddSimpleNpc | ContinentBattleInfo | GetContinentBattleInfo | SendSecurityPacketAndSelfDestruct | GetRespawnLocations | Security | ServerPopulationInfo | GetServerPopulationInfo | VehicleCollision | PlayerStop | ClientSettings | RewardBuffInfo | GetRewardBuffInfo | CharacterSelectSessionResponse | UpdateWeatherData | AddLightweightPc | AddLightweightNpc | AddLightweightVehicle | AddProxiedObject | LightweightToFullPc | LightweightToFullNpc | LightweightToFullVehicle | FairPlayInternal | CharacterRemovePlayer | CharacterKnockback | CharacterUpdateHitpoints | CharacterPlayAnimation | CharacterUpdateScale | CharacterUpdateTemporaryAppearance | CharacterSetLookAt | CharacterRenamePlayer | CharacterUpdateCharacterState | CharacterExpectedSpeed | CharacterThoughtBubble | CharacterSlotCompositeEffectOverride | CharacterPreferredLanguages | CharacterCustomizationChange | CharacterPlayerTitle | CharacterAddEffectTagCompositeEffect | CharacterRemoveEffectTagCompositeEffect | CharacterCustomizeNpc | CharacterSetSpawnerActivationEffect | CharacterSetComboState | CharacterSetSurpriseState | CharacterRemoveNpcCustomization | CharacterReplaceBaseModel | CharacterSetCollidable | CharacterUpdateOwner | CharacterWeaponStance | CharacterUpdateTintAlias | CharacterMoveOnRail | CharacterClearMovementRail | CharacterMoveOnRelativeRail | CharacterDestroyed | CharacterSeekTarget | CharacterSeekTargetUpdate | CharacterUpdateActiveWieldType | CharacterMemberStatus | CharacterKnockedOut | CharacterKnockedOutDamageReport | CharacterRespawn | CharacterRespawnReply | CharacterActivateProfile | CharacterJet | CharacterStartRevive | CharacterStopRevive | CharacterSetFaction | CharacterSetBattleRank | CharacterStartHeal | CharacterStopHeal | CharacterManagedObject | CharacterMaterialTypeOverride | CharacterDebrisLaunch | CharacterHideCorpse | CharacterCharacterStateDelta | CharacterUpdateStat | CharacterAnimationRequest | CharacterPlayWorldCompositeEffect | CharacterAFK | CharacterFullCharacterDataRequest | CharacterDeploy | CharacterKilledBy | CharacterMotorRunning | CharacterDroppedItemNotification | CharacterNoSpaceNotification | CharacterStartMultiStateDeath | CharacterAggroLevel | CharacterDoorState | CharacterSetAllowRespawn | CharacterUpdateGuildTag | CharacterMovementVersion | CharacterDailyRepairMaterials | CharacterUpdateTwitchInfo | CharacterUpdateSimpleProxyHealth | GroupInvite | GroupJoin | GroupAutoGroup | GroupLeave | GroupKick | GroupDisband | GroupSetGroupFlags | GroupSetGroupOwner | GroupSetGroupDescription | GroupUnknownA | GroupMapPingRelated | GroupUnknownC | GroupGetGroup | GroupUnknownF | GroupJoinLookingForMore | GroupToggleSquadLeaderChat | GroupUnknown12 | GroupPlayerJoined | GroupUnknown14 | GroupRemoveGroup | GroupRemoveMember | GroupRemoveInvitation | GroupUnknown19 | GroupUnknown1a | GroupRaidCreate | ReferenceDataItemClassDefinitions | ReferenceDataItemCategoryDefinitions | ReferenceDataProfileDefinitions | ReferenceDataWeaponDefinitions | ReferenceDataProjectileDefinitions | ReferenceDataDynamicAppearance | UiTaskAdd | UiTaskUpdate | UiTaskComplete | UiTaskFail | UiExecuteScript | UiStartTimer | UiObjectiveTargetUpdate | UiMessage | UiCinematicStartLookAt | UiWeaponHitFeedback | UiHeadShotFeedback | UiWaypointCooldown | UiZoneWaypoint | UiWaypointNotify | UiInteractStart | UiRewardNotification | UiWarpgateRotateWarning | UiConfirmHit | RewardAddRewardItem | RewardAddNonRewardItem | RecipeAdd | RecipeComponentUpdate | RecipeRemove | RecipeDiscovery | RecipeDiscoveries | RecipeRequestDiscovery | RecipeUnk8 | RecipeList | InGamePurchaseServerStatusResponse | FriendList | FriendMessage | ClientPathRequest | ClientPathReply | LobbyJoinLobbyGame | LobbyLeaveLobbyGame | LobbyStartLobbyGame | LobbyUpdateLobbyGame | LobbySendLobbyToClient | LobbyLobbyErrorMessage | LobbyShowLobbyUi | FirstTimeEventNotifySystem | FirstTimeEventState | FirstTimeEventUnknown2 | FirstTimeEventUnknown3 | FirstTimeEventScript | FirstTimeEventUnknown4 | GuildDisband | GuildRename | GuildChangeMemberRank | GuildMotdUpdate | AchievementAdd | AchievementInitialize | Loot | MountMountResponse | MountDismountRequest | MountDismountResponse | MountList | MountOfferUpsell | MountSeatChangeRequest | MountSeatChangeResponse | MountSeatSwapRequest | MountFlipMount | TargetUnk7 | TargetUnk8 | TargetUnk9 | TargetUnk10 | TargetUnk11 | TargetUnk12 | Ping | Pong | VoiceLogin | VoiceJoinChannel | VoiceLeaveChannel | VoiceRadioChannel | VoiceLeaveRadio | VoiceUnk9 | WeaponWeapon | FacilityReferenceData | FacilityFacilityData | FacilitySpawnCollisionChanged | SkillSetSkillPointManager | SkillSetSkillPointProgress | LoadoutSelectLoadout | LoadoutUnk1 | LoadoutSetLoadoutSlots | LoadoutSetLoadoutSlot | LoadoutSelectSlot | LoadoutCreateCustomLoadout | ExperienceSetExperienceRanks | ExperienceSetExperienceRateTier | VehicleOwner | VehicleOccupy | VehicleStateData | VehicleStateDamage | VehicleSpawn | VehicleTint | VehicleActiveWeapon | VehicleStats | VehicleDamageInfo | VehicleStatUpdate | VehicleUpdateWeapon | VehicleUpdateQueuePosition | VehicleSetAutoDrive | VehicleLockOnInfo | VehicleLockOnState | VehicleTrackingState | VehicleCounterMeasureState | VehicleLoadVehicleDefinitionManager | VehicleAcquireState | VehicleAutoMount | VehicleDeploy | VehicleEngine | VehicleAccessType | VehicleHealthUpdateOwner | VehicleOwnerPassengerList | VehicleKick | VehicleNoAccess | VehicleExpiration | VehicleGroup | VehicleDeployResponse | VehicleControllerLogOut | VehicleCurrentMoveMode | VehicleItemDefinitionRequest | VehicleItemDefinitionReply | VehicleInventoryItems | ResourceEvent | CollisionDamage | EquipmentSetCharacterEquipment | EquipmentSetCharacterEquipmentSlot | EquipmentUnsetCharacterEquipmentSlot | EquipmentSetCharacterEquipmentSlots | DefinitionFilterSetDefinitionVariable | DefinitionFilterSetDefinitionIntSet | DefinitionFilterUnknownWithVariable1 | DefinitionFilterUnknownWithVariable2 | H1emuPrintToConsole | H1emuMessageBox | H1emuRequestAssetHashes | H1emuVoiceInit | H1emuRequestModules | H1emuRequestWindows | H1emuVoiceState | WallOfDataUIEvent | WallOfDataClientSystemInfo | WallOfDataClientTransition | EffectAddEffect | EffectUpdateEffect | EffectRemoveEffect | EffectAddEffectTag | EffectRemoveUiIndicators | EffectAddUiIndicator | AbilitiesInitAbility | AbilitiesUpdateAbility | AbilitiesUninitAbility | AbilitiesSetActivatableAbilityManager | AbilitiesSetVehicleActivatableAbilityManager | AbilitiesActivateAbility | AbilitiesDeactivateAbility | AbilitiesVehicleDeactivateAbility | AbilitiesActivateAbilityFailed | AbilitiesClearAbilityLineManager | AbilitiesSetProfileAbilityLineMembers | AbilitiesSetLoadoutAbilities | AbilitiesAddLoadoutAbility | AbilitiesAddPersistentAbility | AbilitiesSetProfileRankAbilities | MapRegionGlobalData | MapRegionData | MapRegionMapOutOfBounds | MapRegionRequestContinentData | AcquireTimers | ItemsSetItemTimerManager | ItemsSetItemTrialLockTimer | ItemsSetAccountItemManager | ItemsAddAccountItem | ItemsRemoveAccountItem | ItemsUpdateAccountItem | ItemsSetEscrowAccountItemManager | ItemsAddEscrowAccountItem | ItemsRemoveEscrowAccountItem | ItemsUpdateEscrowAccountItem | ItemsAccountItemManagerStateChanged | ItemsReportNewRewardCrateAdded | ItemsReportRewardCrateContents | ItemsSetEmoteItem | ItemsRemoveEmoteItem | ItemsSetSkinItemManager | ItemsSetSkinItem | ItemsSetCurrentSkinItemCollection | ItemsRequestUseItem | ItemsRequestUseAccountItem | CurrencySetCurrencyDiscount | ZoneSettingData | WordFilterData | StaticFacilityInfoAllZones | OperationClientClearMissions | WordFilterData | StatsUnk2 | StatsUnk3 | StatsUnk4 | StatsAllPlayerStatLeaderboard | StatsPlayersLeaderboard | LocksShowMenu | CharacterStateTimerDataSource | CharacterStateInteractionStart | CharacterStateInteractionStop | CharacterStateUpdateTimerDataSource | AudioSetState | AudioSetSwitch | AudioPostEvent | NpcFoundationPermissionsManagerAddPermission | NpcFoundationPermissionsManagerEditPermission | NpcFoundationPermissionsManagerBaseShowPermissions | ReplicationCreateRepData | ReplicationUpdateRepData | ReplicationCreateComponent | VehicleSkinSetVehicleSkinManager | AnimationRequest | AnimationPlay | ChatChat | ChatEnterArea | ChatDebugChat | ChatFromStringId | ChatChatText | CommandPlaySoundAtLocation | CommandInteractRequest | CommandInteractCancel | CommandInteractDebug | CommandInteractionList | CommandInteractionSelect | CommandSetProfile | CommandPlayerSelect | CommandFreeInteractionNpc | CommandRecipeStart | CommandShowRecipeWindow | CommandPlayDialogEffect | CommandPlaySoundIdOnTarget | CommandInteractionString | CommandAddWorldCommand | CommandAddZoneCommand | CommandExecuteCommand | CommandZoneExecuteCommand | CommandItemDefinitionRequest | CommandItemDefinitionReply | CommandItemDefinitions | CommandEnableCompositeEffects | CommandRequestWeaponFireStateUpdate | CommandDeliveryDisplayInfo | CommandDeliveryManagerStatus | CommandDeliveryManagerShowNotification | CommandReportLastDeath | CommandPointAndReport | CommandSpawnVehicle | CommandRunSpeed | CommandAddItem | ClientUpdateItemAdd | ClientUpdateItemUpdate | ClientUpdateItemDelete | ClientUpdateUpdateStat | ClientUpdateUpdateLocation | ClientUpdateActivateProfile | ClientUpdateDoneSendingPreloadCharacters | ClientUpdateDamageInfo | ClientUpdateRespawnLocations | ClientUpdateModifyMovementSpeed | ClientUpdateModifyTurnRate | ClientUpdateModifyStrafeSpeed | ClientUpdateUpdateManagedLocation | ClientUpdateManagedMovementVersion | ClientUpdateUpdateWeaponAddClips | ClientUpdateStartTimer | ClientUpdateCompleteLogoutProcess | ClientUpdateProximateItems | ClientUpdateTextAlert | ClientUpdateNetworkProximityUpdatesComplete | ClientUpdateDeathMetrics | ClientUpdateManagedObjectResponseControl | ClientUpdateNpcRelevance | ClientUpdateMonitorTimeDrift | ClientUpdateUpdateRewardAndGrinderState | ClientUpdateUpdateLockoutTimes | ClientUpdateZoneStatus | InGamePurchasePreviewOrderResponse | InGamePurchasePlaceOrderResponse | InGamePurchaseStoreBundles | InGamePurchaseStoreBundleCategoryGroups | InGamePurchaseStoreBundleCategories | InGamePurchaseExclusivePartnerStoreBundles | InGamePurchaseStoreBundleGroups | InGamePurchaseWalletInfoResponse | InGamePurchaseStationCashProductsResponse | InGamePurchaseStateCodesResponse | InGamePurchaseCountryCodesResponse | InGamePurchaseSubscriptionProductsResponse | InGamePurchaseEnableMarketplace | InGamePurchaseAccountInfoRequest | InGamePurchaseAccountInfoResponse | InGamePurchaseStoreBundleContentRequest | InGamePurchaseStoreBundleContentResponse | InGamePurchaseClientStatistics | InGamePurchaseDisplayMannequinStoreBundles | InGamePurchaseItemOfTheDay | InGamePurchaseEnablePaymentSources | InGamePurchaseSetMembershipFreeItemInfo | InGamePurchaseGiftOrderNotification | InGamePurchaseActiveSchedules | InGamePurchaseNudgeOfferNotification | InGamePurchaseSpiceWebAuthUrlResponse | InGamePurchaseBundlePriceUpdate | InGamePurchaseWalletBalanceUpdate | InGamePurchaseMemberFreeItemCount | QuickChatSendData | BroadcastLocal | BroadcastZone | BroadcastWorld | LobbyGameDefinitionDefinitionsRequest | LobbyGameDefinitionDefinitionsResponse | CoinStoreItemList | CoinStoreSellToClientRequest | CoinStoreTransactionComplete | ProfileStatsGetPlayerProfileStats | H1emuFairPlay | H1emuHeartBeat | DtoHitReportPacket | DtoStateChange | DtoObjectInitialData | DtoHitSpeedTreeReport | ContainerMoveItem | ContainerInitEquippedContainers | ContainerError | ContainerListAll | ContainerUpdateEquippedContainer | ConstructionPlacementRequest | ConstructionPlacementResponse | ConstructionPlacementFinalizeRequest | ConstructionPlacementFinalizeResponse | ConstructionUnknown | LocksSetLock | RagdollStart | RagdollUpdatePose | RagdollUnk2 | RagdollUnk | RagdollStop | GameModeUpdateToxicGas | GameModeUpdateSafeZone | GameModeDeathInfo | GameModeStartLogout | GameModePlayersRemaining | GameModeTeamsRemaining | GameModeUnk11 | GameModeUnk13 | GameModeUnk15 | GameModeUnk16 | GameModeUnk18 | GameModeUnk19 | GameModeUnk20 | GameModeUnk21 | GameModeStartMatch | GameModeUnk23 | GameModeShowVictoryScreen | GrinderExchangeRequest | GrinderExchangeResponse | ScreenEffectApplyScreenEffect | ScreenEffectRemoveScreenEffect | SpectatorEnable | SpectatorAllSpectators | SpectatorUnknown3 | SpectatorTeleport | SpectatorDeathList | SpectatorSetModerator | SpectatorSetOwner | SpectatorMatchResults | SpectatorUnknown12 | SynchronizedTeleportWaitingForPlayers | SynchronizedTeleportNotifyReady | SynchronizedTeleportPlayersReady | SynchronizedTeleportRelease | AccessedCharacterBeginCharacterAccess | AccessedCharacterEndCharacterAccess | AccessedCharacterUpdateMutatorRights | AccessedCharacterUnknown3 | AccessedCharacterUnknown2 | ShaderParameterOverrideBase; \ No newline at end of file diff --git a/src/types/zoneserver.ts b/src/types/zoneserver.ts index 10081af2be..f8353503c2 100644 --- a/src/types/zoneserver.ts +++ b/src/types/zoneserver.ts @@ -228,123 +228,6 @@ export interface ContainerLootSpawner extends LootSpawner { maxItems: number; } -// ── Loot Table JSON (disk format) ───────────────────────────────────────────── - -export type LootConditionType = - | "in_poi" - | "not_in_poi" - | "poi_tag" - | "not_poi_tag" - | "random_chance" - | "elevation_range" - | "item_density" - | "server_time"; - -export interface LootCondition { - condition: LootConditionType; - - // ── POI conditions ────────────────────────────────────────────────────────── - /** Filter by numeric POI id (used with in_poi / not_in_poi) */ - poi_ids?: number[]; - /** Filter by exact POI name (used with in_poi / not_in_poi) */ - poi_names?: string[]; - /** Filter by POI tag strings (used with poi_tag / not_poi_tag) */ - tags?: string[]; - - // ── random_chance ─────────────────────────────────────────────────────────── - /** 0–100 extra chance roll applied on top of spawnChance */ - chance?: number; - - // ── elevation_range ───────────────────────────────────────────────────────── - /** Minimum world Y (inclusive). Omit for no lower bound. */ - min?: number; - /** Maximum world Y (inclusive). Omit for no upper bound. */ - max?: number; - - // ── item_density ───────────────────────────────────────────────────────────── - /** - * Item definition IDs to count nearby. - * Pool is skipped if count of matching spawned items within `radius` >= `max_count`. - */ - item_ids?: number[]; - /** Maximum number of matching items allowed within `radius` before pool is skipped. */ - max_count?: number; - /** Search radius in world units for item_density check. */ - radius?: number; - - // ── server_time ───────────────────────────────────────────────────────────── - /** - * In-game hour (0–23) when pool becomes active (inclusive). - * Supports wrap-around: hour_min=22, hour_max=4 means 22:00–04:00. - */ - hour_min?: number; - /** In-game hour (0–23) when pool stops being active (inclusive). */ - hour_max?: number; - -} - -export type ItemFunctionType = "set_damage" | "set_count"; - -export interface ItemFunction { - function: ItemFunctionType; - /** - * For set_damage: fraction of the item's current durability (0.0–1.0). - * For set_count: minimum item stack count. - */ - min: number; - /** Max value — used as the upper bound of the random range. */ - max: number; -} - -export interface LootTableEntry { - /** - * Determines how this entry is resolved. - * - "item": spawns a specific item (default when omitted). - * - "loot_table": draws a random item from another named loot table. - * - "empty": produces nothing (useful as a weighted no-spawn slot). - */ - type?: "item" | "loot_table" | "empty"; - /** Item definition ID. Used when type is "item" or absent. */ - item?: number; - /** Name of another loot table to draw from. Used when type is "loot_table". */ - table?: string; - weight: number; - count?: { min: number; max: number }; - /** Functions applied to the spawned item after generation. */ - functions?: ItemFunction[]; -} - -export interface LootPool { - conditions: LootCondition[]; - /** - * How many times this pool draws an entry per spawn cycle (container loot only). - * When omitted, the pool contributes its entries to the legacy maxItems selection. - */ - rolls?: { min: number; max: number }; - entries: LootTableEntry[]; -} - -export interface GroundLootTableJson { - type: "ground"; - spawnChance: number; - pools: LootPool[]; -} - -export interface ContainerLootTableJson { - type: "container"; - pools: LootPool[]; - /** - * Optional top-level spawn chance (0-100). Used by world entities (e.g. crates) - * to decide whether anything spawns at all when the container is broken/opened. - * Not used by the worker for searched container props. - */ - spawnChance?: number; - /** Plugin-only: "append" merges pools with base table; omit/replace overwrites. */ - merge?: "append" | "replace"; -} - -export type LootTableJson = GroundLootTableJson | ContainerLootTableJson; - export interface RecipeComponent { itemDefinitionId: number; requiredAmount: number; diff --git a/src/utils/processErrorHandling.ts b/src/utils/processErrorHandling.ts index f0c39b107e..5322a3681f 100644 --- a/src/utils/processErrorHandling.ts +++ b/src/utils/processErrorHandling.ts @@ -22,14 +22,14 @@ process.on("unhandledRejection", (reason, promise) => { `reason: ${reason} at ${new Date()}` ); logVersion(); - //process.exit(1); + process.exit(1); }); process.on("uncaughtException", (err) => { console.error(`Uncaught Exception: ${err.message} time : ${new Date()}`); console.error(err.stack); logVersion(); - //process.exit(1); + process.exit(1); }); process.on("SIGTERM", () => { diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 125920cde4..70457eec18 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -34,6 +34,12 @@ import { ZoneClient2016 } from "servers/ZoneServer2016/classes/zoneclient"; import * as crypto from "crypto"; import { ZoneClient } from "servers/ZoneServer2015/classes/zoneclient"; import { ConstructionDoor } from "../servers/ZoneServer2016/entities/constructiondoor"; +import { + ConstructionPermissionIds, + ModelIds +} from "../servers/ZoneServer2016/models/enums"; +import { LootableConstructionEntity } from "../servers/ZoneServer2016/entities/lootableconstructionentity"; +import { AddSimpleNpc } from "../types/zone2016packets"; const startTime = Date.now(); @@ -1384,10 +1390,10 @@ export function getConstructionSlotId(buildingSlot: string) { return 1; case "WallStack": return 101; - default: { - const match = buildingSlot.match(/(\d+)$/); - return match ? Number(match[1]) : 0; - } + default: + return Number( + buildingSlot.substring(buildingSlot.length, buildingSlot.length - 2) + ); } } @@ -1684,6 +1690,50 @@ export function luck(l: number) { return Math.floor(Math.random() * l) === 0; } +const Z1_POIs = require("../../data/2016/zoneData/Z1_POIs"); +export function isPosInPoi(position: Float32Array): boolean { + let isInPoi = false; + Z1_POIs.forEach((point: any) => { + let useRange = true; + if (point.bounds) { + useRange = false; + point.bounds.forEach((bound: any) => { + if (isInsideSquare([position[0], position[2]], bound)) { + isInPoi = true; + return; + } + }); + } + if (useRange && isPosInRadius(point.range, position, point.position)) { + isInPoi = true; + } + }); + + return isInPoi; +} + +const Z1_nerfedPOIs = require("../../data/2016/zoneData/Z1_nerfedPOIs"); +export function isLootNerfedLoc(position: Float32Array): number { + let useRange = true; + let nerfedValue = 0; + Z1_nerfedPOIs.forEach((point: any) => { + if (point.bounds) { + useRange = false; + point.bounds.forEach((bound: any) => { + if (isInsideSquare([position[0], position[2]], bound)) { + nerfedValue = point.nerfValue; + return; + } + }); + } + if (useRange && isPosInRadius(point.range, position, point.position)) { + nerfedValue = point.nerfValue; + } + }); + + return nerfedValue; +} + export function chance(chanceNum: number): boolean { return Math.random() * 1000 < chanceNum; } @@ -1715,3 +1765,47 @@ export function quat2heading(quaternion: Float32Array): number { return Math.max(0, Math.min(255, uint8Value)); } + +export function shouldHideHealthBar( + server: ZoneServer2016, + client: ZoneClient2016, + entity: ConstructionChildEntity | LootableConstructionEntity +): Boolean { + const hiddenEntities = + entity.actorModelId == ModelIds.METAL_STORAGE_CHEST || + entity.actorModelId == ModelIds.FURNACE || + entity.actorModelId == 9406 || //workbench + entity.actorModelId == 10065; //weapon workbench + + if (!hiddenEntities) return false; + + const foundation = entity.getParentFoundation(server); + + if (!foundation) return false; + + return !foundation.getHasPermission( + server, + client.character.characterId, + ConstructionPermissionIds.CONTAINERS + ); +} + +export function getSimpleNpcCheckHidden( + server: ZoneServer2016, + client: ZoneClient2016, + entity: ConstructionChildEntity | LootableConstructionEntity +): AddSimpleNpc { + const simpleNpc = { + characterId: entity.characterId, + transientId: entity.transientId, + position: entity.state.position, + rotation: entity.state.rotation, + modelId: entity.actorModelId, + scale: entity.scale, + health: (entity.health / entity.maxHealth) * 100 + }; + + if (shouldHideHealthBar(server, client, entity)) simpleNpc.health = 100; + + return simpleNpc; +}